This commit was manufactured by cvs2svn to create tag 'R3_3_1'.
diff --git a/bundles/org.eclipse.jst.jsp.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.jst.jsp.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..7db8bc6
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.core/META-INF/MANIFEST.MF
@@ -0,0 +1,63 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jst.jsp.core; singleton:=true
+Bundle-Version: 1.2.401.qualifier
+Bundle-Activator: org.eclipse.jst.jsp.core.internal.JSPCorePlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.jst.jsp.core.internal;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.contentmodel;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.contentmodel.tld;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.contentproperties;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.contenttype;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.document;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.domdocument;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.encoding;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.java;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.java.jspel;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.java.search;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.modelhandler;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.modelquery;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.parser;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.parser.internal;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.preferences;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.provisional;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.provisional.contenttype;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.provisional.text;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.regions;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.taglib;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.tasks;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.text;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.util;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.internal.validation;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.core.jspel,
+ org.eclipse.jst.jsp.core.taglib,
+ org.eclipse.jst.jsp.core.text,
+ org.eclipse.jst.jsp.css.core.internal.contentmodel;x-internal:=true,
+ org.eclipse.jst.jsp.css.core.internal.contenttype;x-internal:=true,
+ org.eclipse.jst.jsp.css.core.internal.document;x-internal:=true,
+ org.eclipse.jst.jsp.css.core.internal.encoding;x-internal:=true,
+ org.eclipse.jst.jsp.css.core.internal.modelhandler;x-friends:="org.eclipse.jst.jsp.ui",
+ org.eclipse.jst.jsp.css.core.internal.parser;x-internal:=true,
+ org.eclipse.jst.jsp.css.core.internal.parserz;x-internal:=true
+Import-Package: com.ibm.icu.text;version="3.8",
+ com.ibm.icu.util;version="3.8",
+ javax.servlet.jsp.tagext;version="[2.0.0,2.2.0)"
+Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.jobs;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.filebuffers;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.jdt.core;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.common.uriresolver;bundle-version="[1.1.301,1.2.0)",
+ org.eclipse.wst.sse.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.wst.xml.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.wst.css.core;bundle-version="[1.1.300,1.2.0)",
+ org.eclipse.wst.html.core;bundle-version="[1.1.400,1.2.0)",
+ org.eclipse.wst.validation;bundle-version="[1.2.100,1.3.0)",
+ org.eclipse.wst.common.project.facet.core;bundle-version="[1.4.0,2.0.0)";resolution:=optional,
+ org.eclipse.wst.common.modulecore;bundle-version="[1.1.300,2.0.0)";resolution:=optional,
+ org.eclipse.osgi.services;bundle-version="3.2.0"
+Bundle-ActivationPolicy: lazy; exclude:="org.eclipse.jst.jsp.core.internal.contenttype,org.eclipse.jst.jsp.css.core.internal.contenttype,org.eclipse.jst.jsp.core.internal.provisional.contenttype"
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/bundles/org.eclipse.jst.jsp.core/plugin.xml b/bundles/org.eclipse.jst.jsp.core/plugin.xml
new file mode 100644
index 0000000..4002668
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.core/plugin.xml
@@ -0,0 +1,377 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+   <extension-point id="elTranslator" name="%extension-point.name.0" schema="schema/eltranslator.exsd"/>
+
+	<extension point="org.eclipse.wst.sse.core.modelHandler">
+		<modelHandler
+			class="org.eclipse.jst.jsp.core.internal.modelhandler.ModelHandlerForJSP"
+			associatedContentTypeId="org.eclipse.jst.jsp.core.jspsource"
+			id="org.eclipse.jst.jsp.core.internal.modelhandler">
+		</modelHandler>
+		<modelHandler
+			class="org.eclipse.jst.jsp.core.internal.modelhandler.ModelHandlerForJSP"
+			associatedContentTypeId="org.eclipse.jst.jsp.core.jspfragmentsource"
+			id="org.eclipse.jst.jsp.core.internal.modelhandler">
+		</modelHandler>
+		<modelHandler
+			class="org.eclipse.jst.jsp.core.internal.modelhandler.ModelHandlerForTag"
+			associatedContentTypeId="org.eclipse.jst.jsp.core.tagsource"
+			id="org.eclipse.jst.jsp.core.internal.modelhandler.tag">
+		</modelHandler>
+	</extension>
+
+	<extension point="org.eclipse.wst.xml.core.documentFactories">
+		<factory
+			type="tld"
+			class="org.eclipse.jst.jsp.core.internal.contentmodel.tld.CMDocumentFactoryTLD">
+		</factory>
+	</extension>
+
+	<extension point="org.eclipse.wst.sse.core.taskscanner">
+		<scanner
+			id="org.eclipse.jst.jsp.core.internal.tasks.JSPFileTaskScanner"
+			class="org.eclipse.jst.jsp.core.internal.tasks.JSPFileTaskScanner:org.eclipse.jst.jsp.core.taskMarker"
+			contentTypeIds="org.eclipse.jst.jsp.core.jspsource" />
+	</extension>
+	<extension
+		id="taskMarker"
+		name="%_taskMarker.name"
+		point="org.eclipse.core.resources.markers">
+      <super type="org.eclipse.core.resources.taskmarker"/>
+		<persistent value="true"/>
+	</extension>
+ <extension
+         id="validationMarker"
+         name="%_validationMarker.name"
+         point="org.eclipse.core.resources.markers">
+      <super type="org.eclipse.wst.validation.problemmarker"/>
+      <persistent value="true"/>
+ </extension>
+
+	<extension
+		point="org.eclipse.core.filebuffers.documentCreation"
+		id="org.eclipse.jst.jsp.core.documentfactories"
+		name="%Structured_JSP_Document_Factory_Extension.name">
+		<factory
+			contentTypeId="org.eclipse.jst.jsp.core.jspsource"
+			class="org.eclipse.wst.sse.core.internal.filebuffers.BasicStructuredDocumentFactory" />
+	</extension>
+	<extension
+		point="org.eclipse.core.filebuffers.documentSetup"
+		id="org.eclipse.jst.jsp.core.documentsetup"
+		name="%Structured_JSP_Document_Setup_participant.name">
+		<participant
+			contentTypeId="org.eclipse.jst.jsp.core.jspsource"
+			class="org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController" />
+	</extension>
+	<extension point="org.eclipse.team.core.fileTypes">
+		<fileTypes
+			type="text"
+			extension="jsp">
+		</fileTypes>
+		<fileTypes
+			type="text"
+			extension="jspf" />
+		<fileTypes
+			type="text"
+			extension="jspx" />
+		<fileTypes
+			type="text"
+			extension="jsf">
+		</fileTypes>
+		<fileTypes
+			type="text"
+			extension="jsv">
+		</fileTypes>
+		<fileTypes
+			type="text"
+			extension="jtpl">
+		</fileTypes>
+		<fileTypes
+			type="text"
+			extension="tld" />
+		<fileTypes
+			type="text"
+			extension="tag" />
+		<fileTypes
+			type="text"
+			extension="tagx" />
+		<fileTypes
+			type="text"
+			extension="tagf" />
+	</extension>
+	<extension point="org.eclipse.wst.sse.core.formatProcessors">
+		<processor
+			class="org.eclipse.wst.html.core.internal.format.HTMLFormatProcessorImpl"
+			contentTypeId="org.eclipse.jst.jsp.core.jspsource">
+		</processor>
+	</extension>
+
+	<extension point="org.eclipse.core.contenttype.contentTypes">
+		<!-- create content type for JSP files -->
+		<content-type
+			file-extensions="jsp,jsv,jtpl,jspx"
+			priority="high"
+			name="%JSP_Content_Type_Extension_Element.name"
+			id="org.eclipse.jst.jsp.core.jspsource"
+			base-type="org.eclipse.core.runtime.text"
+			default-charset="ISO-8859-1">
+			<describer
+				class="org.eclipse.jst.jsp.core.internal.contenttype.ContentDescriberForJSP" />
+		</content-type>
+		<!-- create content type for JSP fragment files -->
+		<content-type
+			file-extensions="jspf,jsf"
+			priority="high"
+			name="%JSP_Fragment_Content_Type_Extension_Element.name"
+			id="org.eclipse.jst.jsp.core.jspfragmentsource"
+			base-type="org.eclipse.jst.jsp.core.jspsource"
+			default-charset="ISO-8859-1">
+			<describer
+				class="org.eclipse.jst.jsp.core.internal.contenttype.ContentDescriberForJSP" />
+		</content-type>
+		<!-- create content type for JSP 2.0 Tag file types -->
+		<content-type
+			file-extensions="tag,tagf"
+			priority="high"
+			name="%JSP_Tag_Content_Type_Extension_Element.name"
+			id="org.eclipse.jst.jsp.core.tagsource"
+			base-type="org.eclipse.jst.jsp.core.jspsource"
+			default-charset="ISO-8859-1"/>
+		<!-- create content type for JSP 2.0 TagX file types -->
+		<content-type
+			file-extensions="tagx,tagxf"
+			priority="high"
+			name="%JSP_TagX_Content_Type_Extension_Element.name"
+			id="org.eclipse.jst.jsp.core.tagxsource"
+			base-type="org.eclipse.jst.jsp.core.tagsource"
+			default-charset="ISO-8859-1"/>
+		<!-- create content type for .tld files -->
+		<content-type
+			file-extensions="tld"
+			priority="high"
+			name="%JSP_TLD_Content_Type_Extension_Element.name"
+			id="org.eclipse.jst.jsp.core.tldsource"
+			base-type="org.eclipse.core.runtime.xml"
+			default-charset="UTF-8">
+			<!-- add the describer so it's properly detected when opened from within .jar files -->
+			<describer class="org.eclipse.core.runtime.content.XMLRootElementContentDescriber">
+				<parameter name="element" value="taglib"/>
+			</describer>
+		</content-type>
+		
+
+  	<!--======================================================================================-->
+	<!-- minimal content-type css jsp support                                                 -->
+	<!--======================================================================================-->
+        <content-type
+			file-extensions="jsp"
+			priority="high"
+			name="%contentTypeCSSJSPName"
+			id="org.eclipse.jst.jsp.core.cssjspsource"
+			base-type="org.eclipse.jst.jsp.core.jspsource">
+			<describer
+				class="org.eclipse.jst.jsp.css.core.internal.contenttype.ContentDescriberForJSPedCSS" />
+		</content-type>
+        <content-type
+	        file-extensions="jspf"
+			priority="high"
+			name="%contentTypeCSSJSPFName"
+			id="org.eclipse.jst.jsp.core.cssjspfragmentsource"
+			base-type="org.eclipse.jst.jsp.core.cssjspsource">
+			<describer
+				class="org.eclipse.jst.jsp.css.core.internal.contenttype.ContentDescriberForJSPedCSS" />
+		</content-type>
+	</extension>
+
+	<!-- initialize jsp core preferences -->
+	<extension point="org.eclipse.core.runtime.preferences">
+		<initializer
+			class="org.eclipse.jst.jsp.core.internal.preferences.JSPCorePreferenceInitializer" />
+	</extension>
+
+	<!--======================================================================================-->
+	<!-- JSP batch validation                                       						  -->
+	<!--======================================================================================-->
+   <extension id="JSPBatchValidator" name="%JSP_Syntax_Validator.name" point="org.eclipse.wst.validation.validatorV2">
+      <validator
+            build="true"
+            class="org.eclipse.jst.jsp.core.internal.validation.JSPBatchValidator"
+            manual="true"
+            version="1"
+            markerId="org.eclipse.jst.jsp.core.validationMarker"
+            sourceid="org.eclipse.jst.jsp.jspelsourcevalidator, org.eclipse.jst.jsp.ui.internal.validation.jspactionvalidator, org.eclipse.jst.jsp.tldsourcevalidator, org.eclipse.jst.jsp.jspsourcevalidator">
+         <include>
+            <rules>
+	           <projectNature id="org.eclipse.jdt.core.javanature"/>
+            </rules>
+         </include>
+         <include>
+            <rules>
+				<contentType id="org.eclipse.jst.jsp.core.jspsource"/>
+				<!-- needed until bug 181443 is implemented -->
+				<contentType id="org.eclipse.jst.jsp.core.jspfragmentsource"/>
+				<contentType id="org.eclipse.jst.jsp.core.tagsource"/>
+               <fileext
+                     caseSensitive="false"
+                     ext="jsp">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="jspf">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="jsf">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="tag">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="tagf">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="jsv">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="jtpl">
+               </fileext>
+            </rules>
+         </include>
+         <group id="org.eclipse.wst.sse.core.structuredModelGroup"/>
+      </validator>
+   </extension>
+	
+   <extension id="JSPContentValidator" name="%JSP_Content_Validator.name" point="org.eclipse.wst.validation.validatorV2">
+      <validator
+            build="true"
+            class="org.eclipse.jst.jsp.core.internal.validation.JSPContentValidator"
+            manual="true"
+            version="1"
+            markerId="org.eclipse.jst.jsp.core.validationMarker"
+            sourceid="org.eclipse.jst.jsp.ui.internal.validation.markupvalidator, org.eclipse.jst.jsp.ui.internal.validation.htmlsyntaxvalidator">
+         <include>
+            <rules>
+				<contentType id="org.eclipse.jst.jsp.core.jspsource"/>
+				<!-- needed until bug 181443 is implemented -->
+				<contentType id="org.eclipse.jst.jsp.core.jspfragmentsource"/>
+               <fileext
+                     caseSensitive="false"
+                     ext="jsp">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="jspf">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="jsf">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="tag">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="tagf">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="jsv">
+               </fileext>
+               <fileext
+                     caseSensitive="false"
+                     ext="jtpl">
+               </fileext>
+            </rules>
+         </include>
+         <group id="org.eclipse.wst.sse.core.structuredModelGroup"/>
+      </validator>
+   </extension>
+   
+   <extension id="TLDValidator" name="%TLDValidator.name" point="org.eclipse.wst.validation.validatorV2">
+      <validator
+            build="true"
+            class="org.eclipse.jst.jsp.core.internal.validation.TLDValidator"
+            manual="true"
+            version="1"
+            sourceid="org.eclipse.jst.jsp.ui.internal.validation.jspactionvalidator, org.eclipse.jst.jsp.tldsourcevalidator"
+            markerId="org.eclipse.jst.jsp.core.validationMarker">
+         <include>
+            <rules>
+				<contentType id="org.eclipse.jst.jsp.core.tldsource"/>
+				<contentType id="org.eclipse.jst.jsp.core.tagxsource"/>
+               <fileext caseSensitive="false" ext="tld"/>
+               <fileext caseSensitive="false" ext="tagx"/>
+           </rules>
+         </include>
+         <group id="org.eclipse.wst.sse.core.structuredModelGroup"/>
+      </validator>
+   </extension>
+		
+    <extension
+       point="org.eclipse.jst.jsp.core.elTranslator">
+       <elTranslator
+          class="org.eclipse.jst.jsp.core.internal.java.jspel.JSPELTranslator"
+          name="Default JSP 2.0 EL Translator"
+          id="org.eclipse.jst.jsp.defaultJSP20"/>
+    </extension>
+    
+	<extension point="org.eclipse.wst.sse.core.modelHandler">
+		<modelHandler
+			class="org.eclipse.jst.jsp.css.core.internal.modelhandler.ModelHandlerForJSPedCSS"
+			associatedContentTypeId="org.eclipse.jst.jsp.core.cssjspsource"
+			id="org.eclipse.jst.jsp.css.core.modelhandler">
+		</modelHandler>
+		<modelHandler
+			class="org.eclipse.jst.jsp.css.core.internal.modelhandler.ModelHandlerForJSPedCSS"
+			associatedContentTypeId="org.eclipse.jst.jsp.core.cssjspfragmentsource"
+			id="org.eclipse.jst.jsp.css.core.modelhandler">
+		</modelHandler>
+	</extension>
+	
+	<extension
+		point="org.eclipse.core.filebuffers.documentCreation"
+		id="org.eclipse.jst.jsp.css.core.internal.documentfactories"
+		name="%contentTypeCSSDocumentFactoryName">
+		<factory
+			contentTypeId="org.eclipse.jst.jsp.core.cssjspsource"
+			class="org.eclipse.wst.sse.core.internal.filebuffers.BasicStructuredDocumentFactory" />
+	</extension>
+	<extension
+		point="org.eclipse.core.filebuffers.documentSetup"
+		id="org.eclipse.jst.jsp.css.core.documentsetup"
+		name="%Structured_JSP_Document_Setup_participant.name">
+		<participant
+			contentTypeId="org.eclipse.jst.jsp.core.cssjspsource"
+			class="org.eclipse.jst.jsp.css.core.internal.contentmodel.JSPedCSSTaglibController" />
+	</extension>
+	<!--
+	<extension
+		point="org.eclipse.core.filebuffers.documentCreation"
+		id="org.eclipse.jst.jsp.css.core.internal.documentfactories.fragment"
+		name="%contentTypeCSSDocumentFactoryName">
+		<factory
+			contentTypeId="org.eclipse.jst.jsp.core.cssjspfragmentsource"
+			class="org.eclipse.wst.sse.core.internal.filebuffers.BasicStructuredDocumentFactory" />
+	</extension>	
+	-->
+	<extension point="org.eclipse.wst.xml.core.modelQueryExtensions">
+		<modelQueryExtension
+			class="org.eclipse.jst.jsp.core.internal.modelquery.JSPModelQueryExtension"
+			contentType="org.eclipse.jst.jsp.core.jspsource,org.eclipse.jst.jsp.core.jspfragmentsource,org.eclipse.jst.jsp.core.tagsource">
+		</modelQueryExtension>
+	</extension>
+	<extension point="org.eclipse.wst.xml.core.modelQueryExtensions">
+		<modelQueryExtension
+			class="org.eclipse.jst.jsp.core.internal.modelquery.TaglibModelQueryExtension"
+			contentType="org.eclipse.jst.jsp.core.jspsource,org.eclipse.jst.jsp.core.jspfragmentsource,org.eclipse.jst.jsp.core.tagsource">
+		</modelQueryExtension>
+	</extension>
+
+</plugin>
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contentmodel/tld/CMDocumentFactoryTLD.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contentmodel/tld/CMDocumentFactoryTLD.java
new file mode 100644
index 0000000..efe6126
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/contentmodel/tld/CMDocumentFactoryTLD.java
@@ -0,0 +1,1156 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jst.jsp.core.internal.contentmodel.tld;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jst.jsp.core.internal.Logger;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.JSP11TLDNames;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.JSP12TLDNames;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.JSP20TLDNames;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDDocument;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDElementDeclaration;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDFunction;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDInitParam;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDListener;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDValidator;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDVariable;
+import org.eclipse.jst.jsp.core.internal.modelhandler.ModelHandlerForJSP;
+import org.eclipse.jst.jsp.core.internal.provisional.JSP11Namespace;
+import org.eclipse.jst.jsp.core.internal.provisional.JSP20Namespace;
+import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts;
+import org.eclipse.jst.jsp.core.internal.util.DocumentProvider;
+import org.eclipse.jst.jsp.core.internal.util.FacetModuleCoreSupport;
+import org.eclipse.jst.jsp.core.taglib.IJarRecord;
+import org.eclipse.jst.jsp.core.taglib.ITLDRecord;
+import org.eclipse.jst.jsp.core.taglib.ITagDirRecord;
+import org.eclipse.jst.jsp.core.taglib.ITaglibRecord;
+import org.eclipse.jst.jsp.core.taglib.IURLRecord;
+import org.eclipse.wst.common.uriresolver.internal.util.URIHelper;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
+import org.eclipse.wst.sse.core.internal.util.JarUtilities;
+import org.eclipse.wst.sse.core.utils.StringUtils;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration;
+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.factory.CMDocumentFactory;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * CMDocumentBuilder for Tag Library Descriptors and tag files
+ * 
+ * Returns namespace-less CMDocuments for a tag library descriptor, loading it
+ * directly from a file or extracted from a JAR archive. Content Model objects
+ * will implement the TLDCMDocument, TLDElementDeclaration, and
+ * TLDAttributeDeclaration interfaces for extended properties.
+ */
+public class CMDocumentFactoryTLD implements CMDocumentFactory {
+	static final boolean _debug = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/tldcmdocument/factory")); //$NON-NLS-1$ //$NON-NLS-2$
+
+	/**
+	 * CMDocumentFactoryTLD constructor comment.
+	 */
+	public CMDocumentFactoryTLD() {
+		super();
+	}
+
+	/**
+	 * NOT API
+	 * 
+	 * @param baselocation
+	 * @param input
+	 * @return
+	 */
+	public CMDocument buildCMDocument(String baselocation, InputStream input) {
+		DocumentProvider provider = new DocumentProvider();
+		provider.setValidating(false);
+		provider.setRootElementName(JSP11TLDNames.TAGLIB);
+		provider.setInputStream(input);
+		if (baselocation != null)
+			provider.setBaseReference(baselocation);
+		return loadDocument(baselocation, provider.getRootElement());
+	}
+
+	/**
+	 * @param fileName
+	 * @return
+	 */
+	private CMDocumentImpl buildCMDocumentFromFolder(IPath path) {
+		if (_debug) {
+			System.out.println("tagdir loading for " + path); //$NON-NLS-1$
+		}
+		// EBNF is listed at 1.3.10
+		CMDocumentImpl document = new CMDocumentImpl();
+		document.setBaseLocation(path.toString());
+		document.setTlibversion("1.0"); //$NON-NLS-1$
+		IFolder folder = ResourcesPlugin.getWorkspace().getRoot().getFolder(path);
+		IResource[] tagfiles;
+		try {
+			tagfiles = folder.members();
+			for (int i = 0; i < tagfiles.length; i++) {
+				if (tagfiles[i].getType() == IResource.FILE) {
+					if (tagfiles[i].getType() != IResource.FILE)
+						continue;
+					String extension = tagfiles[i].getFileExtension();
+					if (extension != null && (extension.equals("tag") || extension.equals("tagx"))) {
+						CMElementDeclaration ed = createElementDeclaration(document, (IFile) tagfiles[i]);
+						if (ed != null) {
+							document.fElements.setNamedItem(ed.getNodeName(), ed);
+						}
+					}
+				}
+			}
+		}
+		catch (CoreException e) {
+			Logger.logException(e);
+		}
+		return document;
+	}
+
+	/**
+	 * NOT API
+	 * 
+	 * @param fileName
+	 * @return
+	 */
+	protected CMDocument buildCMDocumentFromFile(String fileName) {
+		// load the taglib descriptor file
+		DocumentProvider provider = new DocumentProvider();
+		provider.setValidating(false);
+		provider.setBaseReference(fileName);
+		provider.setRootElementName(JSP11TLDNames.TAGLIB);
+		provider.setFileName(fileName);
+		Node rootElement = provider.getRootElement();
+		return loadDocument(fileName, rootElement);
+	}
+
+	/**
+	 * Builds a CMDocument assuming the JSP v1.1 default path
+	 * 
+	 * @param jarFileName -
+	 *            the name of the containing JAR file
+	 */
+	protected CMDocument buildCMDocumentFromJar(String jarFileName) {
+		// load the taglib descriptor file
+		return buildCMDocumentFromJar(jarFileName, JarUtilities.JSP11_TAGLIB);
+	}
+
+	/**
+	 * Builds a CMDocument
+	 * 
+	 * @param jarFileName -
+	 *            the name of the containing JAR file
+	 * @param contentFileName -
+	 *            the path within the JAR for a valid taglib descriptor
+	 */
+	protected CMDocument buildCMDocumentFromJar(String jarFileName, String contentFileName) {
+		// load the taglib descriptor file
+		DocumentProvider provider = new DocumentProvider();
+		provider.setValidating(false);
+		provider.setBaseReference(jarFileName);
+		provider.setRootElementName(JSP11TLDNames.TAGLIB);
+		provider.setJarFileName(jarFileName);
+		provider.setFileName(contentFileName);
+		CMDocument document = loadDocument("jar:file://" + jarFileName + "!" + contentFileName, provider.getRootElement()); //$NON-NLS-1$ //$NON-NLS-2$
+		// TODO: Add the tags declared in META-INF/tags, see JSP 2.0 section
+		// 8.4.1
+		return document;
+	}
+
+	protected CMAttributeDeclaration createAttributeDeclaration(CMDocument document, Node attrNode) {
+		CMAttributeDeclarationImpl attr = new CMAttributeDeclarationImpl(document);
+
+		Node child = attrNode.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				if (child.getNodeName().equals(JSP11TLDNames.NAME) && child.hasChildNodes()) {
+					attr.setNodeName(getContainedText(child));
+				}
+				else if (child.getNodeName().equals(JSP12TLDNames.DESCRIPTION) && child.hasChildNodes()) {
+					attr.setDescription(getContainedText(child));
+				}
+				else if (child.getNodeName().equals(JSP11TLDNames.ID) && child.hasChildNodes()) {
+					attr.setId(getContainedText(child));
+				}
+				else if (child.getNodeName().equals(JSP11TLDNames.REQUIRED) && child.hasChildNodes()) {
+					attr.setRequiredString(getContainedText(child));
+				}
+				else if (child.getNodeName().equals(JSP11TLDNames.RTEXPRVALUE) && child.hasChildNodes()) {
+					attr.setRtexprvalue(getContainedText(child));
+				}
+				else if (child.getNodeName().equals(JSP20TLDNames.FRAGMENT) && child.hasChildNodes()) {
+					attr.setFragment(Boolean.valueOf(getContainedText(child)).booleanValue());
+				}
+			}
+			child = child.getNextSibling();
+		}
+
+		return attr;
+	}
+
+	/**
+	 * Builds a CMDocument from a taglib descriptor
+	 * 
+	 * @param uri -
+	 *            the location of a valid taglib descriptor
+	 */
+	public CMDocument createCMDocument(String uri) {
+		CMDocument result = null;
+		URL url = null;
+		try {
+			url = new URL(uri);
+		}
+		catch (MalformedURLException e) {
+			result = createCMDocumentFromFile(uri);
+		}
+		if (result == null && url != null) {
+			if (url.getProtocol().equals("file")) { //$NON-NLS-1$
+				result = createCMDocumentFromFile(url.getFile());
+			}
+			else {
+				/**
+				 * Skip anything else since trying to load a TLD from a remote
+				 * location has unpredictable performance impact.
+				 */
+			}
+		}
+		if (result == null)
+			result = new CMDocumentImpl();
+		return result;
+	}
+
+	/**
+	 * @param fileName
+	 * @return
+	 */
+	private CMDocument createCMDocumentFromFile(String fileName) {
+		CMDocument result = null;
+		if (fileName.endsWith(".jar")) { //$NON-NLS-1$
+			result = buildCMDocumentFromJar(fileName);
+		}
+		else {
+			File file = new File(fileName);
+			try {
+				if (file.isDirectory()) {
+					result = buildCMDocumentFromDirectory(file);
+				}
+				else {
+					result = buildCMDocumentFromFile(fileName);
+				}
+			}
+			catch (SecurityException e) {
+				result = null;
+			}
+		}
+		return result;
+	}
+
+	private CMDocument buildCMDocumentFromDirectory(File file) {
+		IFile[] foundFilesForLocation = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(new Path(file.getPath()));
+		for (int i = 0; i < foundFilesForLocation.length; i++) {
+			if (foundFilesForLocation[i].isAccessible() && foundFilesForLocation[i].getType() == IResource.FOLDER) {
+				return buildCMDocumentFromFolder(foundFilesForLocation[i].getFullPath());
+			}
+		}
+		return null;
+	}
+
+	protected CMElementDeclaration createElementDeclaration(CMDocumentImpl document, Element tagFileNode, String path) {
+		CMElementDeclarationImpl ed = new CMElementDeclarationImpl(document);
+		/*
+		 * Preload with information from the tag file--it can be overwritten
+		 * by the values from the TLD
+		 */
+		final IFile reference = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(new Path(document.getBaseLocation()));
+		IPath tagPath = null;
+		if (reference != null && reference.isAccessible()) {
+			if (!FacetModuleCoreSupport.isDynamicWebProject(reference.getProject())) {
+				tagPath = getExportedTagPath(reference, path);
+			}
+			else {
+				// Even if it's a DWP, it's possible that it might be exported like a Java project when not within WebContent root
+				final IPath root = FacetModuleCoreSupport.getWebContentRootPath(reference.getProject());
+				if (root != null) {
+					// It's not in the WebContent root path
+					if (root.matchingFirstSegments(reference.getFullPath()) != root.segmentCount()) {
+						tagPath = getExportedTagPath(reference, path);
+					}
+				}
+			}
+		}
+		if (tagPath == null) {
+			tagPath = FacetModuleCoreSupport.resolve(new Path(document.getBaseLocation()), path);
+		}
+		if (tagPath.segmentCount() > 1) {
+			IFile tagFile = ResourcesPlugin.getWorkspace().getRoot().getFile(tagPath);
+			if (tagFile.isAccessible()) {
+				ed.setPath(tagFile.getFullPath().toString());
+				if (tagPath.getFileExtension().equals("tag")) {
+					loadTagFile(ed, tagFile, true);
+				}
+				else if (tagPath.getFileExtension().equals("tagx")) {
+					loadTagXFile(ed, tagFile, true);
+				}
+
+				if (tagFile.getLocation() != null && ed.getSmallIcon() != null) {
+					ed.setSmallIconURL(URIHelper.normalize(ed.getSmallIcon(), "file:" + tagFile.getLocation().toString(), tagFile.getLocation().removeLastSegments(1).toString()));
+				}
+			}
+			else if (isJarFile(document.getBaseLocation())) {
+				String jarLocation = document.getBaseLocation();
+				boolean tag = path.endsWith(".tag"); //$NON-NLS-1$
+				InputStream contents = JarUtilities.getInputStream(jarLocation, path);
+				if (tag) {
+					loadTagFile(ed, tagFile, true, contents);
+				}
+				else {
+					loadTagXFile(ed, tagFile, true, contents);
+				}
+				// set the location string properly for this tag (BUG 353615)
+				ed.setLocationString("jar:file://" + document.getBaseLocation() + "!" + path); //$NON-NLS-1$ //$NON-NLS-2$
+				try {
+					contents.close();
+				}
+				catch (IOException e) {
+					Logger.log(Logger.ERROR_DEBUG, null, e);
+				}
+			}
+		}
+
+		// load information declared within the .tld
+		Node child = tagFileNode.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				String nodeName = child.getNodeName();
+				if (nodeName.equals(JSP12TLDNames.DESCRIPTION) && child.hasChildNodes()) {
+					ed.setDescription(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.DISPLAY_NAME) && child.hasChildNodes()) {
+					ed.setDisplayName(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP20TLDNames.EXAMPLE) && child.hasChildNodes()) {
+					ed.setExample(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP20TLDNames.ICON) && child.hasChildNodes()) {
+					ed.setSmallIcon(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.NAME) && child.hasChildNodes()) {
+					ed.setNodeName(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP20TLDNames.PATH) && child.hasChildNodes()) {
+					ed.setPath(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP20TLDNames.TAG_EXTENSION)) {
+					ed.getExtensions().add(child);
+				}
+			}
+			child = child.getNextSibling();
+		}
+		
+		return ed;
+	}
+
+	private IPath getExportedTagPath(IFile file, String path) {
+		final IJavaProject javaProject = JavaCore.create(file.getProject());
+		if (javaProject != null) {
+			try {
+				final IPackageFragmentRoot[] packageFragmentRoots = javaProject.getPackageFragmentRoots();
+				for (int i = 0; i < packageFragmentRoots.length; i++) {
+					if (!packageFragmentRoots[i].isArchive() && !packageFragmentRoots[i].isExternal()) {
+						return packageFragmentRoots[i].getPath().append(new Path(path));
+					}
+				}
+			}
+			catch (JavaModelException e) {
+				Logger.logException(e);
+			}
+		}
+		return null;
+	}
+
+	private boolean isJarFile(String path) {
+		if (path == null)
+			return false;
+		final int idx = path.lastIndexOf('.');
+		return idx >= 0 && idx < (path.length() - 1) && path.substring(idx + 1).equalsIgnoreCase("jar"); //$NON-NLS-1$
+	}
+
+	protected CMElementDeclaration createElementDeclaration(CMDocument document, Node tagNode) {
+		CMElementDeclarationImpl ed = new CMElementDeclarationImpl(document);
+
+		Node child = tagNode.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				// tag information
+				String nodeName = child.getNodeName();
+				if (nodeName.equals(JSP11TLDNames.NAME) && child.hasChildNodes()) {
+					ed.setNodeName(getContainedText(child));
+				}
+				else if ((nodeName.equals(JSP11TLDNames.TAGCLASS) || nodeName.equals(JSP12TLDNames.TAG_CLASS)) && child.hasChildNodes()) {
+					ed.setTagclass(getContainedText(child));
+				}
+				else if ((nodeName.equals(JSP11TLDNames.TEICLASS) || nodeName.equals(JSP12TLDNames.TEI_CLASS)) && child.hasChildNodes()) {
+					ed.setTeiclass(getContainedText(child));
+				}
+				else if ((nodeName.equals(JSP11TLDNames.BODYCONTENT) || nodeName.equals(JSP12TLDNames.BODY_CONTENT)) && child.hasChildNodes()) {
+					String bodycontent = getContainedText(child);
+					// Apparently, Apache Tomcat is not case sensitive about
+					// these values
+					if (bodycontent.equalsIgnoreCase(JSP11TLDNames.CONTENT_JSP))
+						ed.setBodycontent(JSP11TLDNames.CONTENT_JSP);
+					else if (bodycontent.equalsIgnoreCase(JSP11TLDNames.CONTENT_TAGDEPENDENT))
+						ed.setBodycontent(JSP11TLDNames.CONTENT_TAGDEPENDENT);
+					else if (bodycontent.equalsIgnoreCase(JSP11TLDNames.CONTENT_EMPTY))
+						ed.setBodycontent(JSP11TLDNames.CONTENT_EMPTY);
+					else if (bodycontent.equalsIgnoreCase(JSP20TLDNames.CONTENT_SCRIPTLESS))
+						ed.setBodycontent(JSP20TLDNames.CONTENT_SCRIPTLESS);
+				}
+				// info (1.1 only) or description (1.2 only)
+				else if ((nodeName.equals(JSP11TLDNames.INFO) || nodeName.equals(JSP12TLDNames.DESCRIPTION)) && child.hasChildNodes()) {
+					ed.setDescription(getContainedText(child));
+				}
+				// attributes
+				else if (nodeName.equals(JSP11TLDNames.ATTRIBUTE)) {
+					CMAttributeDeclaration attr = createAttributeDeclaration(document, child);
+					ed.fAttributes.setNamedItem(attr.getAttrName(), attr);
+				}
+				// variables
+				else if (nodeName.equals(JSP12TLDNames.VARIABLE)) {
+					ed.getVariables().add(createVariable(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.LARGE_ICON) && child.hasChildNodes()) {
+					ed.setLargeIcon(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.SMALL_ICON) && child.hasChildNodes()) {
+					ed.setSmallIcon(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP20TLDNames.TAG_EXTENSION) && child.getNodeType() == Node.ELEMENT_NODE) {
+					ed.getExtensions().add(child);
+				}
+				else if (nodeName.equals(JSP20TLDNames.DYNAMIC_ATTRIBUTES) && child.hasChildNodes()) {
+					ed.setDynamicAttributes(getContainedText(child));
+				}
+			}
+			child = child.getNextSibling();
+		}
+		return ed;
+	}
+
+	private CMElementDeclaration createElementDeclaration(CMDocument document, IFile tagFile) {
+		CMElementDeclarationImpl ed = new CMElementDeclarationImpl(document);
+		// in tag files, the default body content is scriptless instead of JSP
+		ed.setBodycontent(JSP20TLDNames.CONTENT_SCRIPTLESS);
+		String shortFilename = tagFile.getName();
+		String fileExtension = tagFile.getFileExtension();
+		if (fileExtension != null && fileExtension.length() > 0) {
+			shortFilename = shortFilename.substring(0, shortFilename.length() - fileExtension.length() - 1);
+		}
+		ed.setNodeName(shortFilename);
+		ed.setPath(tagFile.getFullPath().toString());
+		if ("tag".equals(fileExtension)) { //$NON-NLS-1$
+			loadTagFile(ed, tagFile, true);
+		}
+		else if ("tagx".equals(fileExtension)) { //$NON-NLS-1$
+			loadTagXFile(ed, tagFile, true);
+		}
+
+		if (tagFile.getLocation() != null && ed.getSmallIcon() != null) {
+			ed.setSmallIconURL(URIHelper.normalize(ed.getSmallIcon(), "file:" + tagFile.getLocation().toString(), tagFile.getLocation().removeLastSegments(1).toString()));
+		}
+		return ed;
+	}
+
+	protected TLDFunction createFunction(CMDocument document, Node functionNode) {
+		TLDFunctionImpl function = new TLDFunctionImpl(document);
+		boolean hasName = false;
+
+		Node child = functionNode.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				// tag information
+				String nodeName = child.getNodeName();
+				if (nodeName.equals(JSP12TLDNames.DESCRIPTION) && child.hasChildNodes()) {
+					function.setDescription(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.DISPLAY_NAME) && child.hasChildNodes()) {
+					function.setName(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP20TLDNames.EXAMPLE) && child.hasChildNodes()) {
+					function.setExample(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP20TLDNames.FUNCTION_CLASS) && child.hasChildNodes()) {
+					function.setClassName(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP20TLDNames.FUNCTION_EXTENSION) && child.hasChildNodes()) {
+					function.getExtensions().add(child);
+				}
+				else if (nodeName.equals(JSP20TLDNames.FUNCTION_SIGNATURE) && child.hasChildNodes()) {
+					function.setSignature(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP20TLDNames.ICON) && child.hasChildNodes()) {
+					function.setIcon(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.NAME) && child.hasChildNodes()) {
+					function.setName(getContainedText(child));
+					hasName = function.getName().trim().length() > 0;
+				}
+			}
+			child = child.getNextSibling();
+		}
+		if (hasName) {
+			return function;
+		}
+		return null;
+	}
+
+	protected TLDInitParam createInitParam(Node initParamNode) {
+		TLDInitParamImpl initParam = new TLDInitParamImpl();
+		Node child = initParamNode.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				String nodeName = child.getNodeName();
+				if (nodeName.equals(JSP12TLDNames.VALIDATOR_PARAM_NAME) && child.hasChildNodes()) {
+					initParam.setName(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.VALIDATOR_PARAM_VALUE) && child.hasChildNodes()) {
+					initParam.setValue(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.DESCRIPTION) && child.hasChildNodes()) {
+					initParam.setDescription(getContainedText(child));
+				}
+			}
+			child = child.getNextSibling();
+		}
+		return initParam;
+	}
+
+	protected TLDListener createListener(Node listenerNode) {
+		TLDListenerImpl listener = new TLDListenerImpl();
+		Node child = listenerNode.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				String nodeName = child.getNodeName();
+				if (nodeName.equals(JSP12TLDNames.LISTENER_CLASS) && child.hasChildNodes()) {
+					listener.setListenerClass(getContainedText(child));
+				}
+			}
+			child = child.getNextSibling();
+		}
+		return listener;
+	}
+
+	protected TLDValidator createValidator(Node validatorNode) {
+		TLDValidatorImpl validator = new TLDValidatorImpl();
+		Node child = validatorNode.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				String nodeName = child.getNodeName();
+				if (nodeName.equals(JSP12TLDNames.VALIDATOR_CLASS) && child.hasChildNodes()) {
+					validator.setValidatorClass(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.VALIDATOR_INIT_PARAM) && child.hasChildNodes()) {
+					validator.getInitParams().add(createInitParam(child));
+				}
+			}
+			child = child.getNextSibling();
+		}
+		return validator;
+	}
+
+	protected TLDVariable createVariable(Node variableNode) {
+		TLDVariableImpl variable = new TLDVariableImpl();
+		Node child = variableNode.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ELEMENT_NODE) {
+				String nodeName = child.getNodeName();
+				if (nodeName.equals(JSP12TLDNames.VARIABLE_CLASS) && child.hasChildNodes()) {
+					variable.setVariableClass(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.VARIABLE_DECLARE) && child.hasChildNodes()) {
+					variable.setDeclareString(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.VARIABLE_NAME_FROM_ATTRIBUTE) && child.hasChildNodes()) {
+					variable.setNameFromAttribute(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.VARIABLE_NAME_GIVEN) && child.hasChildNodes()) {
+					variable.setNameGiven(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.VARIABLE_SCOPE) && child.hasChildNodes()) {
+					variable.setScope(getContainedText(child));
+				}
+				else if (nodeName.equals(JSP12TLDNames.DESCRIPTION) && child.hasChildNodes()) {
+					variable.setDescription(getContainedText(child));
+				}
+			}
+			child = child.getNextSibling();
+		}
+		return variable;
+	}
+
+	protected String getContainedText(Node parent) {
+		NodeList children = parent.getChildNodes();
+		if (children.getLength() == 1) {
+			return getValue(children.item(0));
+		}
+		StringBuffer s = new StringBuffer();
+		Node child = parent.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
+				String reference = ((EntityReference) child).getNodeValue();
+				if (reference == null && child.getNodeName() != null) {
+					reference = "&" + child.getNodeName() + ";"; //$NON-NLS-1$ //$NON-NLS-2$
+				}
+				if (reference != null) {
+					s.append(reference.trim());
+				}
+			}
+			else {
+				s.append(getValue(child));
+			}
+			child = child.getNextSibling();
+		}
+		return s.toString().trim();
+	}
+
+	private String getValue(Node n) {
+		if (n == null)
+			return ""; //$NON-NLS-1$
+		String value = n.getNodeValue();
+		if (value == null)
+			return ""; //$NON-NLS-1$
+		return value.trim();
+	}
+
+	public boolean isBuilderForGrammar(String grammarFileName) {
+		String fileName = grammarFileName.toLowerCase();
+		return fileName.endsWith(".tld") || fileName.endsWith(".jar"); //$NON-NLS-2$//$NON-NLS-1$
+	}
+
+	private CMDocument loadDocument(String baseLocation, Node taglib) {
+		Node root = taglib;
+
+		// create the CMDocument
+		CMDocumentImpl document = new CMDocumentImpl();
+		document.setBaseLocation(baseLocation);
+
+		if (root == null) {
+			if (_debug) {
+				System.out.println("null \"taglib\" element for TLD " + baseLocation); //$NON-NLS-1$
+			}
+			return document;
+		}
+
+		// populate the CMDocument
+		Node child = root.getFirstChild();
+		while (child != null) {
+			if (child.getNodeType() != Node.ELEMENT_NODE) {
+				child = child.getNextSibling();
+				continue;
+			}
+			String nodeName = child.getNodeName();
+			if(nodeName.indexOf(':') > 0) {
+				nodeName = nodeName.substring(nodeName.indexOf(':'));
+			}
+			// tag
+			if (nodeName.equals(JSP11TLDNames.TAG)) {
+				CMElementDeclaration ed = createElementDeclaration(document, child);
+				if (ed != null) {
+					document.fElements.setNamedItem(ed.getNodeName(), ed);
+				}
+			}
+			// tag-file
+			else if (nodeName.equals(JSP20TLDNames.TAG_FILE) && child.getNodeType() == Node.ELEMENT_NODE && child.hasChildNodes()) {
+				Element tagFileElement = (Element) child;
+				Node path = tagFileElement.getFirstChild();
+				while (path != null) {
+					if (path.getNodeType() == Node.ELEMENT_NODE && (JSP20TLDNames.PATH.equals(path.getNodeName()) || JSP20TLDNames.PATH.equals(path.getLocalName()))) {
+						String pathValue = getContainedText(path);
+						if (pathValue != null && pathValue.length() > 0) {
+							CMElementDeclarationImpl ed = (CMElementDeclarationImpl) createElementDeclaration(document, tagFileElement, pathValue);
+							if (ed != null) {
+								document.fElements.setNamedItem(ed.getNodeName(), ed);
+							}
+						}
+					}
+					path = path.getNextSibling();
+				}
+			}
+			// other one-of-a-kind children
+			// JSP version
+			else if ((nodeName.equals(JSP11TLDNames.JSPVERSION) || nodeName.equals(JSP12TLDNames.JSP_VERSION)) && child.hasChildNodes()) {
+				document.setJspversion(getContainedText(child));
+			}
+			// tag library version
+			else if ((nodeName.equals(JSP11TLDNames.TLIBVERSION) || nodeName.equals(JSP12TLDNames.TLIB_VERSION)) && child.hasChildNodes()) {
+				document.setTlibversion(getContainedText(child));
+			}
+			// short name
+			else if ((nodeName.equals(JSP11TLDNames.SHORTNAME) || nodeName.equals(JSP12TLDNames.SHORT_NAME)) && child.hasChildNodes()) {
+				document.setShortname(getContainedText(child));
+			}
+			// URI/URN
+			else if ((nodeName.equals(JSP11TLDNames.URI) || nodeName.equals(JSP11TLDNames.URN)) && child.hasChildNodes()) { //$NON-NLS-1$
+				document.setUri(getContainedText(child));
+			}
+			// info
+			else if (nodeName.equals(JSP11TLDNames.INFO) && child.hasChildNodes()) {
+				document.setInfo(getContainedText(child));
+			}
+			// New JSP 1.2
+			// description
+			else if (nodeName.equals(JSP12TLDNames.DESCRIPTION) && child.hasChildNodes()) {
+				document.setDescription(getContainedText(child));
+			}
+			// display name
+			else if (nodeName.equals(JSP12TLDNames.DISPLAY_NAME) && child.hasChildNodes()) {
+				document.setDisplayName(getContainedText(child));
+			}
+			// large icon
+			else if (nodeName.equals(JSP12TLDNames.LARGE_ICON) && child.hasChildNodes()) {
+				document.setLargeIcon(getContainedText(child));
+			}
+			// small icon
+			else if (nodeName.equals(JSP12TLDNames.SMALL_ICON) && child.hasChildNodes()) {
+				document.setSmallIcon(getContainedText(child));
+			}
+			// validator
+			else if (nodeName.equals(JSP12TLDNames.VALIDATOR)) {
+				document.setValidator(createValidator(child));
+			}
+			// listener
+			else if (nodeName.equals(JSP12TLDNames.LISTENER)) {
+				document.getListeners().add(createListener(child));
+			}
+			else if (nodeName.equals(JSP20TLDNames.FUNCTION)) {
+				TLDFunction function = createFunction(document, child);
+				if (function != null) {
+					document.getFunctions().add(function);
+				}
+			}
+			else if (nodeName.equals(JSP20TLDNames.TAGLIB_EXTENSION)) {
+				document.getExtensions().add(child);
+			}
+
+			child = child.getNextSibling();
+		}
+		return document;
+	}
+
+	void loadTagXFile(final CMElementDeclarationImpl ed, IFile tagxFile, boolean allowIncludes) {
+	  loadTagXFile(ed, tagxFile, allowIncludes, null);
+	}
+	
+	void loadTagXFile(final CMElementDeclarationImpl ed, IFile tagxFile, boolean allowIncludes, InputStream inputStream) {
+		ed.setPath(tagxFile.getFullPath().toString());
+		ed.setTagSource(TLDElementDeclaration.SOURCE_TAG_FILE);
+		try {
+			SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+			InputSource inputSource = new InputSource(tagxFile.getFullPath().toString());
+			InputStream input = inputStream != null ? inputStream : tagxFile.getContents(false);
+			inputSource.setByteStream(input);
+			parser.parse(inputSource, new DefaultHandler() {
+				public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
+					InputSource inputSource2 = new InputSource(systemId);
+					inputSource2.setByteStream(new ByteArrayInputStream(new byte[0]));
+					return inputSource2;
+				}
+
+				public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+					super.startElement(uri, localName, qName, attributes);
+					if (qName.equals(JSP20Namespace.ElementName.DIRECTIVE_TAG)) {
+						if (attributes.getIndex(JSP12TLDNames.DISPLAY_NAME) >= 0)
+							ed.setDisplayName(attributes.getValue(JSP12TLDNames.DISPLAY_NAME));
+						if (attributes.getIndex(JSP12TLDNames.BODY_CONTENT) >= 0)
+							ed.setBodycontent(attributes.getValue(JSP12TLDNames.BODY_CONTENT));
+						if (attributes.getIndex(JSP20TLDNames.DYNAMIC_ATTRIBUTES) >= 0)
+							ed.setDynamicAttributes(attributes.getValue(JSP20TLDNames.DYNAMIC_ATTRIBUTES));
+						if (attributes.getIndex(JSP12TLDNames.SMALL_ICON) >= 0)
+							ed.setSmallIcon(attributes.getValue(JSP12TLDNames.SMALL_ICON));
+						if (attributes.getIndex(JSP12TLDNames.LARGE_ICON) >= 0)
+							ed.setLargeIcon(attributes.getValue(JSP12TLDNames.LARGE_ICON));
+						if (attributes.getIndex(JSP12TLDNames.DESCRIPTION) >= 0)
+							ed.setDescription(attributes.getValue(JSP12TLDNames.DESCRIPTION));
+						if (attributes.getIndex(JSP20TLDNames.EXAMPLE) >= 0)
+							ed.setExample(attributes.getValue(JSP20TLDNames.EXAMPLE));
+						if (attributes.getIndex(JSP20TLDNames.SCRIPTING_LANGUAGE) >= 0)
+							ed.setScriptingLanguage(attributes.getValue(JSP20TLDNames.SCRIPTING_LANGUAGE));
+						if (attributes.getIndex(JSP20TLDNames.IMPORT) >= 0)
+							ed.setImport(attributes.getValue(JSP20TLDNames.IMPORT));
+						if (attributes.getIndex(JSP20TLDNames.PAGE_ENCODING) >= 0)
+							ed.setPageEncoding(attributes.getValue(JSP20TLDNames.PAGE_ENCODING));
+						if (attributes.getIndex(JSP20TLDNames.IS_EL_IGNORED) >= 0)
+							ed.setIsELIgnored(attributes.getValue(JSP20TLDNames.IS_EL_IGNORED));
+					}
+					else if (qName.equals(JSP20Namespace.ElementName.DIRECTIVE_ATTRIBUTE)) {
+						CMAttributeDeclarationImpl attribute = new CMAttributeDeclarationImpl(ed.getOwnerDocument());
+						String nameValue = attributes.getValue(JSP12TLDNames.NAME);
+						attribute.setNodeName(nameValue);
+						if (attributes.getIndex(JSP20TLDNames.FRAGMENT) >= 0)
+							attribute.setFragment(Boolean.valueOf(attributes.getValue(JSP20TLDNames.FRAGMENT)).booleanValue());
+						if (attributes.getIndex(JSP12TLDNames.RTEXPRVALUE) >= 0)
+							attribute.setRtexprvalue(attributes.getValue(JSP12TLDNames.RTEXPRVALUE));
+						if (attributes.getIndex(JSP20TLDNames.TYPE) >= 0)
+							attribute.setType(attributes.getValue(JSP20TLDNames.TYPE));
+						if (attributes.getIndex(JSP12TLDNames.DESCRIPTION) >= 0)
+							attribute.setDescription(attributes.getValue(JSP12TLDNames.DESCRIPTION));
+						if (attributes.getIndex(JSP12TLDNames.REQUIRED) >= 0)
+							attribute.setRequiredString(attributes.getValue(JSP12TLDNames.REQUIRED));
+						if (nameValue != null && nameValue.length() > 0) {
+							ed.fAttributes.setNamedItem(nameValue, attribute);
+						}
+					}
+					else if (qName.equals(JSP20Namespace.ElementName.DIRECTIVE_VARIABLE)) {
+						TLDVariableImpl variable = new TLDVariableImpl();
+						if (attributes.getIndex(JSP12TLDNames.VARIABLE_NAME_GIVEN) >= 0)
+							variable.setNameGiven(attributes.getValue(JSP12TLDNames.VARIABLE_NAME_GIVEN));
+						if (attributes.getIndex(JSP12TLDNames.VARIABLE_NAME_FROM_ATTRIBUTE) >= 0)
+							variable.setNameFromAttribute(attributes.getValue(JSP12TLDNames.VARIABLE_NAME_FROM_ATTRIBUTE));
+						if (attributes.getIndex(JSP20TLDNames.VARIABLE_ALIAS) >= 0)
+							variable.setAlias(attributes.getValue(JSP20TLDNames.VARIABLE_ALIAS));
+						if (attributes.getIndex(JSP12TLDNames.VARIABLE_CLASS) >= 0)
+							variable.setVariableClass(attributes.getValue(JSP12TLDNames.VARIABLE_CLASS));
+						if (attributes.getIndex(JSP12TLDNames.VARIABLE_DECLARE) >= 0)
+							variable.setDeclareString(attributes.getValue(JSP12TLDNames.VARIABLE_DECLARE));
+						if (attributes.getIndex(JSP11Namespace.ATTR_NAME_SCOPE) >= 0)
+							variable.setScope(attributes.getValue(JSP11Namespace.ATTR_NAME_SCOPE));
+						if (attributes.getIndex(JSP12TLDNames.DESCRIPTION) >= 0)
+							variable.setDescription(attributes.getValue(JSP12TLDNames.DESCRIPTION));
+						if (variable.getAlias() != null || variable.getNameFromAttribute() != null || variable.getNameGiven() != null) {
+							ed.getVariables().add(variable);
+						}
+					}
+					else if (qName.equals(JSP11Namespace.ElementName.DIRECTIVE_INCLUDE)) {
+						IPath filePath = null;
+						String text = attributes.getValue(JSP11Namespace.ATTR_NAME_FILE);
+						if (text != null) {
+							filePath = FacetModuleCoreSupport.resolve(new Path(((CMDocumentImpl) ed.getOwnerDocument()).getBaseLocation()), text);
+							IFile includedFile = ResourcesPlugin.getWorkspace().getRoot().getFile(filePath);
+							if (includedFile.isAccessible()) {
+								loadTagXFile(ed, includedFile, false);
+							}
+						}
+					}
+				}
+			});
+			input.close();
+		}
+		catch (ParserConfigurationException e) {
+			Logger.log(Logger.ERROR_DEBUG, e.getMessage(), e);
+		}
+		catch (SAXException e) {
+			Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
+		}
+		catch (IOException e) {
+			Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
+		}
+		catch (CoreException e) {
+			Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
+		}
+		ed.setLocationString(tagxFile.getFullPath().toString());
+	}
+
+	private void loadTagFile(CMElementDeclarationImpl ed, IFile tagFile, boolean allowIncludes) {
+		loadTagFile(ed, tagFile, allowIncludes, null);
+	}
+	
+	private void loadTagFile(CMElementDeclarationImpl ed, IFile tagFile, boolean allowIncludes, InputStream inputStream) {
+		try {
+			ed.setPath(tagFile.getFullPath().toString());
+			ed.setTagSource(TLDElementDeclaration.SOURCE_TAG_FILE);
+			ed.setLocationString(tagFile.getFullPath().toString());
+			IStructuredDocument document = null;
+			if(inputStream != null) {
+				document = (IStructuredDocument)new ModelHandlerForJSP().getDocumentLoader().createNewStructuredDocument(tagFile.getName(), inputStream);
+			}
+			else if(tagFile.isAccessible()) {
+				document = (IStructuredDocument) new ModelHandlerForJSP().getDocumentLoader().createNewStructuredDocument(tagFile);
+			}
+			if (document == null)
+				return;
+			IStructuredDocumentRegion documentRegion = document.getFirstStructuredDocumentRegion();
+			while (documentRegion != null) {
+				if (documentRegion.getType().equals(DOMJSPRegionContexts.JSP_DIRECTIVE_NAME)) {
+					if (documentRegion.getNumberOfRegions() > 2) {
+						ITextRegionList regions = documentRegion.getRegions();
+						String directiveName = documentRegion.getText(regions.get(1));
+						if (JSP12TLDNames.TAG.equals(directiveName)) {
+							// 8.5.1
+							String attrName = null;
+							for (int i = 2; i < documentRegion.getNumberOfRegions(); i++) {
+								ITextRegion region = regions.get(i);
+								String text = documentRegion.getText(region);
+								if (region.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
+									attrName = text;
+								}
+								// process value
+								else if (region.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
+									text = StringUtils.strip(text);
+									if (JSP12TLDNames.DISPLAY_NAME.equals(attrName)) {
+										ed.setDisplayName(text);
+									}
+									else if (JSP12TLDNames.BODY_CONTENT.equals(attrName)) {
+										ed.setBodycontent(text);
+									}
+									else if (JSP20TLDNames.DYNAMIC_ATTRIBUTES.equals(attrName)) {
+										ed.setDynamicAttributes(text);
+									}
+									else if (JSP12TLDNames.SMALL_ICON.equals(attrName)) {
+										ed.setSmallIcon(text);
+									}
+									else if (JSP12TLDNames.LARGE_ICON.equals(attrName)) {
+										ed.setLargeIcon(text);
+									}
+									else if (JSP12TLDNames.DESCRIPTION.equals(attrName)) {
+										ed.setDescription(text);
+									}
+									else if (JSP20TLDNames.EXAMPLE.equals(attrName)) {
+										ed.setExample(text);
+									}
+									else if (JSP20TLDNames.SCRIPTING_LANGUAGE.equals(attrName)) {
+										ed.setScriptingLanguage(text);
+									}
+									else if (JSP20TLDNames.IMPORT.equals(attrName)) {
+										ed.setImport(text);
+									}
+									else if (JSP20TLDNames.PAGE_ENCODING.equals(attrName)) {
+										ed.setPageEncoding(text);
+									}
+									else if (JSP20TLDNames.IS_EL_IGNORED.equals(attrName)) {
+										ed.setIsELIgnored(text);
+									}
+								}
+							}
+						}
+						else if (JSP12TLDNames.ATTRIBUTE.equals(directiveName)) {
+							CMAttributeDeclarationImpl attribute = new CMAttributeDeclarationImpl(ed.getOwnerDocument());
+							// 8.5.2
+							String attrName = null;
+							for (int i = 2; i < documentRegion.getNumberOfRegions(); i++) {
+								ITextRegion region = regions.get(i);
+								String text = documentRegion.getText(region);
+								if (region.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
+									attrName = text;
+								}
+								// process value
+								else if (region.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE && attrName != null) {
+									text = StringUtils.strip(text);
+									if (JSP12TLDNames.NAME.equals(attrName)) {
+										attribute.setNodeName(text);
+									}
+									else if (JSP20TLDNames.FRAGMENT.equals(attrName)) {
+										attribute.setFragment(Boolean.valueOf(text).booleanValue());
+									}
+									else if (JSP12TLDNames.RTEXPRVALUE.equals(attrName)) {
+										attribute.setRtexprvalue(text);
+									}
+									else if (JSP20TLDNames.TYPE.equals(attrName)) {
+										attribute.setType(text);
+									}
+									else if (JSP12TLDNames.DESCRIPTION.equals(attrName)) {
+										attribute.setDescription(text);
+									}
+									else if (JSP12TLDNames.REQUIRED.equals(attrName)) {
+										attribute.setRequiredString(text);
+									}
+								}
+							}
+							if (attribute.getNodeName() != null) {
+								ed.fAttributes.setNamedItem(attribute.getNodeName(), attribute);
+							}
+						}
+						else if (JSP12TLDNames.VARIABLE.equals(directiveName)) {
+							TLDVariableImpl variable = new TLDVariableImpl();
+							String attrName = null;
+							for (int i = 2; i < documentRegion.getNumberOfRegions(); i++) {
+								ITextRegion region = regions.get(i);
+								String text = documentRegion.getText(region);
+								if (region.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
+									attrName = text;
+								}
+								// process value
+								else if (region.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE && attrName != null) {
+									text = StringUtils.strip(text);
+									if (JSP12TLDNames.VARIABLE_NAME_GIVEN.equals(attrName)) {
+										variable.setNameGiven(text);
+									}
+									else if (JSP12TLDNames.VARIABLE_NAME_FROM_ATTRIBUTE.equals(attrName)) {
+										variable.setNameFromAttribute(text);
+									}
+									else if (JSP20TLDNames.VARIABLE_ALIAS.equals(attrName)) {
+										variable.setAlias(text);
+									}
+									else if (JSP12TLDNames.VARIABLE_CLASS.equals(attrName)) {
+										variable.setVariableClass(text);
+									}
+									else if (JSP12TLDNames.VARIABLE_DECLARE.equals(attrName)) {
+										variable.setDeclareString(text);
+									}
+									else if (JSP11Namespace.ATTR_NAME_SCOPE.equals(attrName)) {
+										variable.setScope(text);
+									}
+									else if (JSP12TLDNames.DESCRIPTION.equals(attrName)) {
+										variable.setDescription(text);
+									}
+								}
+							}
+							if (variable.getAlias() != null || variable.getNameFromAttribute() != null || variable.getNameGiven() != null) {
+								ed.getVariables().add(variable);
+							}
+						}
+						else if ("include".equals(directiveName) && allowIncludes) {
+							String attrName = null;
+							for (int i = 2; i < documentRegion.getNumberOfRegions(); i++) {
+								ITextRegion region = regions.get(i);
+								String text = documentRegion.getText(region);
+								if (region.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
+									attrName = text;
+								}
+								// process value
+								else if (region.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE && attrName != null) {
+									text = StringUtils.strip(text);
+									if (JSP11Namespace.ATTR_NAME_FILE.equals(attrName)) {
+										IPath filePath = FacetModuleCoreSupport.resolve(new Path(((CMDocumentImpl) ed.getOwnerDocument()).getBaseLocation()), text);
+
+										IFile includedFile = ResourcesPlugin.getWorkspace().getRoot().getFile(filePath);
+										if (includedFile.isAccessible()) {
+											loadTagFile(ed, includedFile, false);
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+
+				documentRegion = documentRegion.getNext();
+			}
+
+		}
+		catch (IOException e) {
+			// Logger.logException("problem parsing " + tagFile, e); // can be caused by a still-in-development file
+		}
+		catch (CoreException e) {
+			// Logger.logException("problem parsing " + tagFile, e); // frequently out of sync
+		}
+	}
+
+	/**
+	 * @param reference
+	 * @return
+	 */
+	public CMDocument createCMDocument(ITaglibRecord reference) {
+		CMDocumentImpl document = null;
+		switch (reference.getRecordType()) {
+			case (ITaglibRecord.TLD) : {
+				ITLDRecord record = (ITLDRecord) reference;
+				IResource file = ResourcesPlugin.getWorkspace().getRoot().getFile(record.getPath());
+				if (file.getLocation() != null) {
+					document = (CMDocumentImpl) buildCMDocumentFromFile(file.getLocation().toString());
+					document.setLocationString(record.getPath().toString());
+					if (_debug && document != null && document.getElements().getLength() == 0) {
+						System.out.println("failure parsing " + record.getPath()); //$NON-NLS-1$
+					}
+
+					if (document.getSmallIcon() != null) {
+						String iconPath = URIHelper.normalize(((TLDDocument) document).getSmallIcon(), file.getLocation().toString(), "/"); //$NON-NLS-1$
+						document.setProperty(JSP12TLDNames.SMALL_ICON, "file:" + iconPath); //$NON-NLS-1$
+					}
+					if (document.getLargeIcon() != null) {
+						String iconPath = URIHelper.normalize(((TLDDocument) document).getLargeIcon(), file.getLocation().toString(), "/"); //$NON-NLS-1$
+						document.setProperty(JSP12TLDNames.LARGE_ICON, "file:" + iconPath); //$NON-NLS-1$
+					}
+				}
+			}
+				break;
+			case (ITaglibRecord.JAR) : {
+				IJarRecord record = (IJarRecord) reference;
+				document = (CMDocumentImpl) buildCMDocumentFromJar(record.getLocation().toString());
+				document.setLocationString("jar:file:" + record.getLocation().toString() + "!/META-INF/taglib.tld");
+				if (document.getSmallIcon() != null) {
+					String iconPath = URIHelper.normalize(((TLDDocument) document).getSmallIcon(), record.getLocation().toString() + "!/META-INF/", "/"); //$NON-NLS-1$ //$NON-NLS-2$
+					document.setProperty(JSP12TLDNames.SMALL_ICON, "jar:file:" + iconPath); //$NON-NLS-1$
+				}
+				if (document.getLargeIcon() != null) {
+					String iconPath = URIHelper.normalize(((TLDDocument) document).getLargeIcon(), record.getLocation().toString() + "!/META-INF/", "/"); //$NON-NLS-1$ //$NON-NLS-2$
+					document.setProperty(JSP12TLDNames.LARGE_ICON, "jar:file:" + iconPath); //$NON-NLS-1$
+				}
+				if (_debug && document != null && document.getElements().getLength() == 0) {
+					System.out.println("failure parsing " + record.getLocation()); //$NON-NLS-1$
+				}
+			}
+				break;
+			case (ITaglibRecord.TAGDIR) : {
+				ITagDirRecord record = (ITagDirRecord) reference;
+				document = buildCMDocumentFromFolder(record.getPath());
+			}
+				break;
+			case (ITaglibRecord.URL) : {
+				IURLRecord record = (IURLRecord) reference;
+				URL url = record.getURL();
+				InputStream urlContents = JarUtilities.getInputStream(url);
+				if (urlContents != null) {
+					document = (CMDocumentImpl) buildCMDocument(record.getBaseLocation(), urlContents);
+					String urlString = url.toString();
+					document.setLocationString(urlString);
+					if (document.getSmallIcon() != null) {
+						String iconPath = URIHelper.normalize(((TLDDocument) document).getSmallIcon(), urlString, "/"); //$NON-NLS-1$
+						document.setProperty(JSP12TLDNames.SMALL_ICON, iconPath);
+					}
+					if (document.getLargeIcon() != null) {
+						String iconPath = URIHelper.normalize(((TLDDocument) document).getLargeIcon(), urlString, "/"); //$NON-NLS-1$
+						document.setProperty(JSP12TLDNames.LARGE_ICON, iconPath);
+					}
+				}
+				if (urlContents != null) {
+					try {
+						urlContents.close();
+					}
+					catch (IOException e) {
+					}
+				}
+			}
+				break;
+		}
+		return document;
+	}
+}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/domdocument/AttrImplForJSP.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/domdocument/AttrImplForJSP.java
new file mode 100644
index 0000000..7dc153d
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/domdocument/AttrImplForJSP.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     
+ *******************************************************************************/
+
+package org.eclipse.jst.jsp.core.internal.domdocument;
+
+import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts;
+import org.eclipse.wst.xml.core.internal.document.AttrImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.UserDataHandler;
+
+public class AttrImplForJSP extends AttrImpl {
+
+	protected AttrImplForJSP() {
+	}
+
+	protected AttrImplForJSP(AttrImplForJSP that) {
+		super(that);
+	}
+
+	protected boolean isNestedLanguageOpening(String regionType) {
+		boolean result = regionType == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN || regionType == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN || regionType == DOMJSPRegionContexts.JSP_DECLARATION_OPEN || regionType == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN;
+		return result;
+	}
+	protected void setOwnerDocument(Document ownerDocument) {
+		super.setOwnerDocument(ownerDocument);
+	}
+	protected void setName(String name) {
+		super.setName(name);
+	}
+	protected void setNamespaceURI(String namespaceURI) {
+		super.setNamespaceURI(namespaceURI);
+	}
+
+	public Node cloneNode(boolean deep) {
+		Node cloned = new AttrImplForJSP(this);
+		notifyUserDataHandlers(UserDataHandler.NODE_CLONED, cloned);
+		return cloned;
+	}
+}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/domdocument/TextImplForJSP.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/domdocument/TextImplForJSP.java
new file mode 100644
index 0000000..b44737f
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/domdocument/TextImplForJSP.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     
+ *******************************************************************************/
+
+package org.eclipse.jst.jsp.core.internal.domdocument;
+
+import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts;
+import org.eclipse.wst.xml.core.internal.document.TextImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.UserDataHandler;
+
+public class TextImplForJSP extends TextImpl {
+
+	protected TextImplForJSP() {
+	}
+
+	protected TextImplForJSP(TextImplForJSP that) {
+		super(that);
+	}
+
+	protected boolean isNotNestedContent(String regionType) {
+		boolean result = regionType != DOMJSPRegionContexts.JSP_CONTENT;
+		return result;
+	}
+	protected void setOwnerDocument(Document ownerDocument) {
+		super.setOwnerDocument(ownerDocument);
+	}
+
+	public Node cloneNode(boolean deep) {
+		Node cloned = new TextImplForJSP(this);
+		notifyUserDataHandlers(UserDataHandler.NODE_CLONED, cloned);
+		return cloned;
+	}
+
+}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
new file mode 100644
index 0000000..747fd36
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslator.java
@@ -0,0 +1,3600 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - contributions for bug 150794
+ *******************************************************************************/
+package org.eclipse.jst.jsp.core.internal.java;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Stack;
+
+import javax.servlet.jsp.tagext.VariableInfo;
+
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
+import org.eclipse.jst.jsp.core.internal.Logger;
+import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TLDCMDocumentManager;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.JSP12TLDNames;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDElementDeclaration;
+import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache;
+import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache.PropertyGroup;
+import org.eclipse.jst.jsp.core.internal.provisional.JSP11Namespace;
+import org.eclipse.jst.jsp.core.internal.provisional.JSP12Namespace;
+import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts;
+import org.eclipse.jst.jsp.core.internal.taglib.CustomTag;
+import org.eclipse.jst.jsp.core.internal.taglib.TaglibHelper;
+import org.eclipse.jst.jsp.core.internal.taglib.TaglibHelperManager;
+import org.eclipse.jst.jsp.core.internal.taglib.TaglibVariable;
+import org.eclipse.jst.jsp.core.internal.util.FacetModuleCoreSupport;
+import org.eclipse.jst.jsp.core.internal.util.ZeroStructuredDocumentRegion;
+import org.eclipse.jst.jsp.core.jspel.IJSPELTranslator;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.html.core.internal.contentmodel.JSP20Namespace;
+import org.eclipse.wst.sse.core.internal.FileBufferModelManager;
+import org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
+import org.eclipse.wst.sse.core.utils.StringUtils;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMNode;
+import org.eclipse.wst.xml.core.internal.parser.ContextRegionContainer;
+import org.eclipse.wst.xml.core.internal.provisional.contentmodel.CMDocumentTracker;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+
+import com.ibm.icu.text.MessageFormat;
+import com.ibm.icu.util.StringTokenizer;
+
+/**
+ * Translates a JSP document into a HttpServlet subclass. Keeps two way mapping from
+ * Java translation to the original JSP source, which can be obtained through
+ * getJava2JspRanges() and getJsp2JavaRanges().
+ */
+public class JSPTranslator implements Externalizable {
+	/**
+	 * <p>This value should be incremented if any of the following methods change:
+	 * <ul>
+	 * <li>{@link #writeExternal(ObjectOutput)}</li>
+	 * <li>{@link #readExternal(ObjectInput)}</li>
+	 * <li>{@link #writeString(ObjectOutput, String)}</li>
+	 * <li>{@link #readString(ObjectInput)}</li>
+	 * <li>{@link #writeRanges(ObjectOutput, HashMap)}</li>
+	 * <li>{@link #readRanges(ObjectInput)}</li>
+	 * </ul>
+	 * 
+	 * This is because if any of these change then previously externalized {@link JSPTranslator}s
+	 * will no longer be able to be read by the new implementation.  This value is used by
+	 * the {@link Externalizable} API automatically to determine if the file being read is of the
+	 * correct version to be read by the current implementation of the {@link JSPTranslator}</p>
+	 * 
+	 * @see #writeExternal(ObjectOutput)
+	 * @see #readExternal(ObjectInput)
+	 * @see #writeString(ObjectOutput, String)
+	 * @see #readString(ObjectInput)
+	 * @see #writeRanges(ObjectOutput, HashMap)
+	 * @see #readRanges(ObjectInput)
+	 */
+	private static final long serialVersionUID = 2L;
+	
+	/** for debugging */
+	private static final boolean DEBUG = Boolean.valueOf(Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/jspjavamapping")).booleanValue(); //$NON-NLS-1$
+	
+	/** handy plugin ID constant */
+	private static final String JSP_CORE_PLUGIN_ID = "org.eclipse.jst.jsp.core"; //$NON-NLS-1$
+	
+	// constants for reading extension point
+	/** Default EL Translator extension ID */
+	private static final String DEFAULT_JSP_EL_TRANSLATOR_ID = "org.eclipse.jst.jsp.defaultJSP20"; //$NON-NLS-1$
+	
+	/** the name of the element in the extension point */
+	private static final String EL_TRANSLATOR_EXTENSION_NAME = "elTranslator"; //$NON-NLS-1$
+	
+	/** the name of the property in the extension point */
+	private static final String ELTRANSLATOR_PROP_NAME = "ELTranslator"; //$NON-NLS-1$
+
+	
+	// these constants are commonly used strings during translation
+	/** end line characters */
+	public static final String ENDL = "\n"; //$NON-NLS-1$
+	
+	/** session variable declaration */
+	private static final String SESSION_VARIABLE_DECLARATION = "javax.servlet.http.HttpSession session = pageContext.getSession();" + ENDL; //$NON-NLS-1$
+	
+	/** footer text */
+	private static final String FOOTER = "}}"; //$NON-NLS-1$
+	
+	/** exception declaration */
+	private static final String EXCEPTION = "Throwable exception = null;"; //$NON-NLS-1$
+	
+	/** expression prefix */
+	public static final String EXPRESSION_PREFIX = "out.print("; //$NON-NLS-1$
+	
+	/** expression suffix */
+	public static final String EXPRESSION_SUFFIX = ");"; //$NON-NLS-1$
+	
+	/** try/catch start */
+	private static final String TRY_CATCH_START = ENDL + "try {" + ENDL; //$NON-NLS-1$
+	
+	/** try/catch end */
+	private static final String TRY_CATCH_END = " } catch (java.lang.Exception e) {} " + ENDL; //$NON-NLS-1$
+	
+	/** JSP tag name prefix */
+	static final String JSP_PREFIX = "jsp:"; //$NON-NLS-1$
+	
+	
+	// these constants are to keep track of what type of code is currently being translated
+	/** code in question is standard JSP */
+	protected final static int STANDARD_JSP = 0;
+	
+	/** code in question is embedded (JSP as an attribute or within comment tags) */
+	protected final static int EMBEDDED_JSP = 1;
+	
+	/** code in question is a JSP declaration */
+	protected final static int DECLARATION = 2;
+	
+	/** code in question is a JSP expression */
+	protected final static int EXPRESSION = 4;
+	
+	/** code in question is a JSP scriptlet */
+	protected final static int SCRIPTLET = 8;
+	
+	
+	// strings specific to this translation
+	/** translated class header */
+	String fClassHeader = null;
+	
+	/** translated class name */
+	String fClassname = null;
+	
+	/** translated class super class */
+	String fSuperclass = null;
+
+	/** translated class imports */
+	String fImplicitImports = null;
+
+	/** translated class service header */
+	String fServiceHeader = null;
+	
+	/** translated user defined imports */
+	private StringBuffer fUserImports = new StringBuffer();
+	
+	//translation specific state
+	/** {@link IDOMModel} for the JSP file being translated */
+	IDOMModel fStructuredModel = null;
+	
+	/** {@link IStructuredDocument} for the JSP file being translated */
+	IStructuredDocument fStructuredDocument = null;
+	
+	/** the EL translator */
+	private IJSPELTranslator fELTranslator = null;
+	
+	/** reported translation problems */
+	private List fTranslationProblems = new ArrayList();
+	
+	/** fSourcePosition = position in JSP source */
+	private int fSourcePosition = -1;
+	
+	/** fRelativeOffest = offset in the buffer there the cursor is */
+	private int fRelativeOffset = -1;
+	
+	/** fCursorPosition = offset in the translated java document */
+	private int fCursorPosition = -1;
+
+	/** some page directive attributes */
+	private boolean fIsErrorPage = false;
+	private boolean fCursorInExpression = false;
+	private boolean fIsInASession = true;
+
+	/** user java code in body of the service method */
+	private StringBuffer fUserCode = new StringBuffer();
+	/** user EL Expression */
+	private StringBuffer fUserELExpressions = new StringBuffer();
+	/** user defined vars declared in the beginning of the class */
+	private StringBuffer fUserDeclarations = new StringBuffer();
+
+	/**
+	 * A map of tag names to tag library variable information; used to store
+	 * the ones needed for AT_END variable support.
+	 */
+	private StackMap fTagToVariableMap = null;
+	private Stack fUseBeansStack = new Stack();
+
+	/** the final translated java document */
+	private StringBuffer fResult;
+	
+	/** the buffer where the cursor is */
+	private StringBuffer fCursorOwner = null;
+
+	private IStructuredDocumentRegion fCurrentNode;
+	
+	/** flag for if the cursor is in the current regionb eing translated */
+	private boolean fInCodeRegion = false;
+
+	/** used to avoid infinite looping include files */
+	private Stack fIncludes = null;
+	private Set fIncludedPaths = new HashSet(2);
+	private boolean fProcessIncludes = true;
+	/** mostly for helper classes, so they parse correctly */
+	private ArrayList fBlockMarkers = null;
+	/**
+	 * for keeping track of offset in user buffers while document is being
+	 * built
+	 */
+	private int fOffsetInUserImports = 0;
+	private int fOffsetInUserDeclarations = 0;
+	private int fOffsetInUserCode = 0;
+
+	/** correlates ranges (positions) in java to ranges in jsp */
+	private HashMap fJava2JspRanges = new HashMap();
+
+	/**
+	 * map of ranges in fUserImports (relative to the start of the buffer) to
+	 * ranges in source JSP buffer.
+	 */
+	private HashMap fImportRanges = new HashMap();
+	/**
+	 * map of ranges in fUserCode (relative to the start of the buffer) to
+	 * ranges in source JSP buffer.
+	 */
+	private HashMap fCodeRanges = new HashMap();
+	/**
+	 * map of ranges in fUserDeclarations (relative to the start of the
+	 * buffer) to ranges in source JSP buffer.
+	 */
+	private HashMap fDeclarationRanges = new HashMap();
+
+	private HashMap fUseBeanRanges = new HashMap();
+
+	private HashMap fUserELRanges = new HashMap();
+
+	/**
+	 * ranges that don't directly map from java code to JSP code (eg.
+	 * <%@include file="included.jsp"%>
+	 */
+	private HashMap fIndirectRanges = new HashMap();
+
+	private IProgressMonitor fProgressMonitor = null;
+
+	/**
+	 * save JSP document text for later use may just want to read this from
+	 * the file or strucdtured document depending what is available
+	 */
+	private StringBuffer fJspTextBuffer = new StringBuffer();
+
+	/** EL Translator ID (pluggable) */
+	private String fELTranslatorID;
+
+	/**
+	 * <code>true</code> if code has been found, such as HTML tags, that is not translated
+	 * <code>false</code> otherwise.  Useful for deciding if a place holder needs to be
+	 * written to translation
+	 */
+	private boolean fFoundNonTranslatedCode;
+
+	/**
+	 * <code>true</code> if code has been translated for the current region,
+	 * <code>false</code> otherwise
+	 */
+	private boolean fCodeTranslated;
+
+	/**
+	 * A structure for holding a region collection marker and list of variable
+	 * information. The region can be used later for positioning validation
+	 * messages.
+	 */
+	static class RegionTags {
+		ITextRegionCollection region;
+		CustomTag tag;
+
+		RegionTags(ITextRegionCollection region, CustomTag tag) {
+			this.region = region;
+			this.tag = tag;
+		}
+	}
+
+	public JSPTranslator() {
+		init();
+	}
+
+	/**
+	 * configure using an XMLNode
+	 * 
+	 * @param node
+	 * @param monitor
+	 */
+	private void configure(IDOMNode node, IProgressMonitor monitor) {
+
+		fProgressMonitor = monitor;
+		fStructuredModel = node.getModel();
+		String baseLocation = fStructuredModel.getBaseLocation();
+
+		fELTranslatorID = getELTranslatorProperty(baseLocation);
+
+		fStructuredDocument = fStructuredModel.getStructuredDocument();
+
+		String className = createClassname(node);
+		if (className.length() > 0) {
+			setClassname(className);
+			fClassHeader = "public class " + className + " extends "; //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+	}
+
+	/**
+	 * memory saving configure (no StructuredDocument in memory) currently
+	 * doesn't handle included files
+	 * 
+	 * @param jspFile
+	 * @param monitor
+	 */
+	private void configure(IFile jspFile, IProgressMonitor monitor) {
+		// when configured on a file
+		// fStructuredModel, fPositionNode, fModelQuery, fStructuredDocument
+		// are all null
+		fProgressMonitor = monitor;
+
+		fELTranslatorID = getELTranslatorProperty(jspFile);
+
+		String className = createClassname(jspFile);
+		if (className.length() > 0) {
+			setClassname(className);
+			fClassHeader = "public class " + className + " extends "; //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	/**
+	 * Set the jsp text from an IFile
+	 * 
+	 * @param jspFile
+	 */
+	private void setJspText(IFile jspFile) {
+		try {
+			BufferedInputStream in = new BufferedInputStream(jspFile.getContents());
+			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+			String line = null;
+			while ((line = reader.readLine()) != null) {
+				fJspTextBuffer.append(line);
+				fJspTextBuffer.append(ENDL);
+			}
+			reader.close();
+		}
+		catch (CoreException e) {
+			Logger.logException(e);
+		}
+		catch (IOException e) {
+			Logger.logException(e);
+		}
+	}
+
+	/**
+	 * Get the value of the ELTranslator property from a workspace relative
+	 * path string
+	 * 
+	 * @param baseLocation
+	 *            Workspace-relative string path
+	 * @return Value of the ELTranslator property associated with the project.
+	 */
+	private String getELTranslatorProperty(String baseLocation) {
+		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+		String elTranslatorValue = null;
+		IFile file = workspaceRoot.getFile(new Path(baseLocation));
+		if (file != null) {
+			elTranslatorValue = getELTranslatorProperty(file);
+		}
+		return elTranslatorValue;
+	}
+
+	/**
+	 * Get the value of the ELTranslator property from an IFile
+	 * 
+	 * @param file
+	 *            IFile
+	 * @return Value of the ELTranslator property associated with the project.
+	 */
+	private String getELTranslatorProperty(IFile file) {
+		String elTranslatorValue = null;
+		if (file != null) {
+			if (file.exists()) {
+				try {
+					elTranslatorValue = file.getPersistentProperty(new QualifiedName(JSP_CORE_PLUGIN_ID, ELTRANSLATOR_PROP_NAME));
+					if (null == elTranslatorValue) {
+
+						elTranslatorValue = file.getProject().getPersistentProperty(new QualifiedName(JSP_CORE_PLUGIN_ID, ELTRANSLATOR_PROP_NAME));
+					}
+				}
+				catch (CoreException e) {
+					// ISSUE: why do we log this here? Instead of allowing to
+					// throwup?
+					Logger.logException(e);
+				}
+
+			}
+		}
+		return elTranslatorValue;
+	}
+
+	/**
+	 * @param node
+	 * @return the simple class name, not fully qualified
+	 */
+	private String createClassname(IDOMNode node) {
+		String classname = ""; //$NON-NLS-1$
+		if (node != null) {
+			String base = node.getModel().getBaseLocation();
+			classname = JSP2ServletNameUtil.mangle(base);
+		}
+		return classname;
+	}
+
+	/**
+	 * @param jspFile
+	 * @return
+	 */
+	private String createClassname(IFile jspFile) {
+
+		String classname = ""; //$NON-NLS-1$
+		if (jspFile != null) {
+			classname = JSP2ServletNameUtil.mangle(jspFile.getFullPath().toString());
+		}
+		return classname;
+	}
+
+	private IJSPProblem createJSPProblem(final int problemEID, final int problemID, final String message, final int start, final int end) {
+		final int line = fStructuredDocument.getLineOfOffset(start);
+		final char[] classname = fClassname.toCharArray();
+
+		/*
+		 * Note: these problems would result in translation errors on the
+		 * server, so the severity is not meant to be controllable
+		 */
+		return new IJSPProblem() {
+			public void setSourceStart(int sourceStart) {
+			}
+
+			public void setSourceLineNumber(int lineNumber) {
+			}
+
+			public void setSourceEnd(int sourceEnd) {
+			}
+
+			public boolean isWarning() {
+				return false;
+			}
+
+			public boolean isError() {
+				return true;
+			}
+
+			public int getSourceStart() {
+				return start;
+			}
+
+			public int getSourceLineNumber() {
+				return line;
+			}
+
+			public int getSourceEnd() {
+				return end;
+			}
+
+			public char[] getOriginatingFileName() {
+				return classname;
+			}
+
+			public String getMessage() {
+				return message;
+			}
+
+			public int getID() {
+				return problemID;
+			}
+
+			public String[] getArguments() {
+				return new String[0];
+			}
+
+			public int getEID() {
+				return problemEID;
+			}
+		};
+	}
+
+	public void setClassname(String classname) {
+		this.fClassname = classname;
+	}
+
+	public String getClassname() {
+		return this.fClassname != null ? this.fClassname : "GenericJspServlet"; //$NON-NLS-1$
+	}
+
+	/**
+	 * So that the JSPTranslator can be reused.
+	 */
+	public void reset(IDOMNode node, IProgressMonitor progress) {
+
+		// initialize some things on node
+		configure(node, progress);
+		reset();
+		// set the jsp text buffer
+		fJspTextBuffer.append(fStructuredDocument.get());
+	}
+
+	/**
+	 * conservative version (no StructuredDocument/Model)
+	 * 
+	 * @param jspFile
+	 * @param progress
+	 */
+	public void reset(IFile jspFile, IProgressMonitor progress) {
+
+		// initialize some things on node
+		configure(jspFile, progress);
+		reset();
+		// set the jsp text buffer
+		setJspText(jspFile);
+	}
+
+	/**
+	 * Reinitialize some fields
+	 */
+	void reset() {
+
+		// reset progress monitor
+		if (fProgressMonitor != null)
+			fProgressMonitor.setCanceled(false);
+
+		// reinit fields
+		fSourcePosition = -1;
+		fRelativeOffset = -1;
+		fCursorPosition = -1;
+
+		fIsErrorPage = fCursorInExpression = false;
+		fIsInASession = true;
+
+		fUserCode = new StringBuffer();
+		fUserDeclarations = new StringBuffer();
+		fUserImports = new StringBuffer();
+		fUserELExpressions = new StringBuffer();
+
+		fResult = null;
+		fCursorOwner = null; // the buffer where the cursor is
+
+		fCurrentNode = null;
+		fInCodeRegion = false; // flag for if cursor is in the current region
+		// being translated
+
+		if (fIncludes != null)
+			fIncludes.clear();
+
+		fBlockMarkers = null;
+
+		fOffsetInUserImports = 0;
+		fOffsetInUserDeclarations = 0;
+		fOffsetInUserCode = 0;
+
+		fJava2JspRanges.clear();
+		fImportRanges.clear();
+		fCodeRanges.clear();
+		fUseBeanRanges.clear();
+		fDeclarationRanges.clear();
+		fUserELRanges.clear();
+		fIndirectRanges.clear();
+		fIncludedPaths.clear();
+
+		fJspTextBuffer = new StringBuffer();
+		
+		fFoundNonTranslatedCode = false;
+		fCodeTranslated = false;
+
+	}
+
+	/**
+	 * @return just the "shell" of a servlet, nothing contributed from the JSP
+	 *         doc
+	 */
+	public final StringBuffer getEmptyTranslation() {
+		reset();
+		buildResult(true);
+		return getTranslation();
+	}
+
+	/**
+	 * <p>put the final java document together</p>
+	 * 
+	 * @param updateRanges <code>true</code> if the ranges need to be updated as the result
+	 * is built, <code>false</code> if the ranges have already been updated.  This is useful
+	 * if building a result from a persisted {@link JSPTranslator}.
+	 */
+	private final void buildResult(boolean updateRanges) {
+		// to build the java document this is the order:
+		// 
+		// + default imports
+		// + user imports
+		// + class header
+		// [+ error page]
+		// + user declarations
+		// + service method header
+		// + try/catch start
+		// + user code
+		// + try/catch end
+		// + service method footer
+		fResult = new StringBuffer(fImplicitImports.length() + fUserImports.length() + fClassHeader.length() +
+				fUserDeclarations.length() + fServiceHeader.length() + TRY_CATCH_START.length()
+				+ fUserCode.length() + TRY_CATCH_END.length() + FOOTER.length());
+
+		int javaOffset = 0;
+
+		fResult.append(fImplicitImports);
+		javaOffset += fImplicitImports.length();
+
+		// updateRanges(fIndirectImports, javaOffset);
+		if(updateRanges) {
+			updateRanges(fImportRanges, javaOffset);
+		}
+		// user imports
+		append(fUserImports);
+		javaOffset += fUserImports.length();
+
+		// class header
+		fResult.append(fClassHeader);
+		javaOffset += fClassHeader.length();
+		fResult.append(fSuperclass + "{" + ENDL); //$NON-NLS-1$
+		javaOffset += fSuperclass.length() + 2;
+
+		if(updateRanges) {
+			updateRanges(fDeclarationRanges, javaOffset);
+		}
+		// user declarations
+		append(fUserDeclarations);
+		javaOffset += fUserDeclarations.length();
+
+		if(updateRanges) {
+			updateRanges(fUserELRanges, javaOffset);
+		}
+		append(fUserELExpressions);
+		javaOffset += fUserELExpressions.length();
+
+		fResult.append(fServiceHeader);
+		javaOffset += fServiceHeader.length();
+		// session participant
+		if (fIsInASession) {
+			fResult.append(SESSION_VARIABLE_DECLARATION);
+			javaOffset += SESSION_VARIABLE_DECLARATION.length();
+		}
+		// error page
+		if (fIsErrorPage) {
+			fResult.append(EXCEPTION);
+			javaOffset += EXCEPTION.length();
+		}
+
+
+		fResult.append(TRY_CATCH_START);
+		javaOffset += TRY_CATCH_START.length();
+
+		if(updateRanges) {
+			updateRanges(fCodeRanges, javaOffset);
+		}
+
+		// user code
+		append(fUserCode);
+		javaOffset += fUserCode.length();
+
+
+		fResult.append(TRY_CATCH_END);
+		javaOffset += TRY_CATCH_END.length();
+
+		// footer
+		fResult.append(FOOTER);
+		javaOffset += FOOTER.length();
+
+		fJava2JspRanges.putAll(fImportRanges);
+		fJava2JspRanges.putAll(fDeclarationRanges);
+		fJava2JspRanges.putAll(fCodeRanges);
+		fJava2JspRanges.putAll(fUserELRanges);
+
+	}
+
+	/**
+	 * @param javaRanges
+	 * @param offsetInJava
+	 */
+	private void updateRanges(HashMap rangeMap, int offsetInJava) {
+		// just need to update java ranges w/ the offset we now know
+		Iterator it = rangeMap.keySet().iterator();
+		while (it.hasNext())
+			((Position) it.next()).offset += offsetInJava;
+	}
+
+	/**
+	 * map of ranges (positions) in java document to ranges in jsp document
+	 * 
+	 * @return a map of java positions to jsp positions.
+	 */
+	public HashMap getJava2JspRanges() {
+		return fJava2JspRanges;
+	}
+
+	/**
+	 * map of ranges in jsp document to ranges in java document.
+	 * 
+	 * @return a map of jsp positions to java positions, or null if no
+	 *         translation has occured yet (the map hasn't been built).
+	 */
+	public HashMap getJsp2JavaRanges() {
+		if (fJava2JspRanges == null)
+			return null;
+		HashMap flipFlopped = new HashMap();
+		Iterator keys = fJava2JspRanges.keySet().iterator();
+		Object range = null;
+		while (keys.hasNext()) {
+			range = keys.next();
+			flipFlopped.put(fJava2JspRanges.get(range), range);
+		}
+		return flipFlopped;
+	}
+
+	public HashMap getJava2JspImportRanges() {
+		return fImportRanges;
+	}
+
+	public HashMap getJava2JspUseBeanRanges() {
+		return fUseBeanRanges;
+	}
+
+	public HashMap getJava2JspIndirectRanges() {
+		return fIndirectRanges;
+	}
+
+	/**
+	 * Adds to the jsp<->java map by default
+	 * 
+	 * @param value
+	 *            a comma delimited list of imports
+	 */
+	protected void addImports(String value) {
+		addImports(value, true);
+	}
+
+	/**
+	 * Pass in a comma delimited list of import values, appends each to the
+	 * final result buffer
+	 * 
+	 * @param value
+	 *            a comma delimited list of imports
+	 */
+	protected void addImports(String value, boolean addToMap) {
+		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=81687
+		// added the "addToMap" parameter to exclude imports originating
+		// from included JSP files to be added to the jsp<->java mapping
+		StringTokenizer st = new StringTokenizer(value, ",", false); //$NON-NLS-1$
+		String tok = ""; //$NON-NLS-1$
+		// String appendage = ""; //$NON-NLS-1$
+		while (st.hasMoreTokens()) {
+			tok = st.nextToken();
+			appendImportToBuffer(tok, fCurrentNode, addToMap);
+		}
+	}
+
+	/**
+	 * /* keep track of cursor position inside the buffer /* appends buffer to
+	 * the final result buffer
+	 */
+	protected void append(StringBuffer buf) {
+		if (getCursorOwner() == buf) {
+			fCursorPosition = fResult.length() + getRelativeOffset();
+		}
+		fResult.append(buf.toString());
+	}
+
+	/**
+	 * Only valid after a configure(...), translate(...) or
+	 * translateFromFile(...) call
+	 * 
+	 * @return the current result (java translation) buffer
+	 */
+	public final StringBuffer getTranslation() {
+
+		if (DEBUG) {
+			StringBuffer debugString = new StringBuffer();
+			try {
+				Iterator it = fJava2JspRanges.keySet().iterator();
+				while (it.hasNext()) {
+					debugString.append("--------------------------------------------------------------\n"); //$NON-NLS-1$
+					Position java = (Position) it.next();
+					debugString.append("Java range:[" + java.offset + ":" + java.length + "]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+					debugString.append("[" + fResult.toString().substring(java.offset, java.offset + java.length) + "]\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					debugString.append("--------------------------------------------------------------\n"); //$NON-NLS-1$
+					debugString.append("|maps to...|\n"); //$NON-NLS-1$
+					debugString.append("==============================================================\n"); //$NON-NLS-1$
+					Position jsp = (Position) fJava2JspRanges.get(java);
+					debugString.append("JSP range:[" + jsp.offset + ":" + jsp.length + "]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+					debugString.append("[" + fJspTextBuffer.toString().substring(jsp.offset, jsp.offset + jsp.length) + "]\n"); //$NON-NLS-1$ //$NON-NLS-2$
+					debugString.append("==============================================================\n"); //$NON-NLS-1$
+					debugString.append("\n"); //$NON-NLS-1$
+					debugString.append("\n"); //$NON-NLS-1$
+				}
+			}
+			catch (Exception e) {
+				Logger.logException("JSPTranslation error", e); //$NON-NLS-1$
+			}
+			Logger.log(Logger.INFO_DEBUG, debugString.toString());
+		}
+
+		return fResult;
+	}
+
+	public List getTranslationProblems() {
+		return fTranslationProblems;
+	}
+
+	/**
+	 * Only valid after a configure(...), translate(...) or
+	 * translateFromFile(...) call
+	 * 
+	 * @return the text in the JSP file
+	 */
+	public final String getJspText() {
+		return fJspTextBuffer.toString();
+	}
+
+	
+	protected void addTaglibVariables(String tagToAdd, ITextRegionCollection customTag) {
+		addTaglibVariables(tagToAdd, customTag, -1);
+	}
+	/**
+	 * Add the server-side scripting variables used by this tag, along with
+	 * any scoping.
+	 * 
+	 * @param tagToAdd
+	 * @param customTag
+	 */
+	protected void addTaglibVariables(String tagToAdd, ITextRegionCollection customTag, int index) {
+		if (customTag.getFirstRegion().getType().equals(DOMRegionContext.XML_TAG_OPEN)) {
+			/*
+			 * Start tag
+			 */
+			addStartTagVariable(tagToAdd, customTag,index);
+		}
+		else if (customTag.getFirstRegion().getType().equals(DOMRegionContext.XML_END_TAG_OPEN)) {
+			/*
+			 * End tag
+			 */
+			addEndTagVariable(tagToAdd, customTag);
+		}
+	}
+	
+	private void addEndTagVariable(String tagToAdd, ITextRegionCollection customTag){
+		IFile f = getFile();
+		if (f == null || !f.exists())
+			return;
+		String decl = ""; //$NON-NLS-1$
+		RegionTags regionTag = (RegionTags) fTagToVariableMap.pop(tagToAdd);
+		if (regionTag != null) {
+			// even an empty array will indicate a need for a closing brace
+			TaglibVariable[] taglibVars = regionTag.tag.getTagVariables();
+			StringBuffer text = new StringBuffer();
+			if (regionTag.tag.isIterationTag())
+				doAfterBody(text, regionTag);
+			text.append("} // </"); //$NON-NLS-1$
+			text.append(tagToAdd);
+			text.append(">\n"); //$NON-NLS-1$
+			appendToBuffer(text.toString(), fUserCode, false, customTag);
+			for (int i = 0; i < taglibVars.length; i++) {
+				if (taglibVars[i].getScope() == VariableInfo.AT_END) {
+					decl = taglibVars[i].getDeclarationString();
+					appendToBuffer(decl, fUserCode, false, customTag);
+				}
+			}
+		}
+		else {
+			/*
+			 * Since something should have been in the map because of a
+			 * start tag, its absence now means an unbalanced end tag.
+			 * Extras will be checked later to flag unbalanced start tags.
+			 */
+			IJSPProblem missingStartTag = createJSPProblem(IJSPProblem.StartCustomTagMissing, IJSPProblem.F_PROBLEM_ID_LITERAL, NLS.bind(JSPCoreMessages.JSPTranslator_4, tagToAdd), customTag.getStartOffset(), customTag.getEndOffset());
+			fTranslationProblems.add(missingStartTag);
+		}
+	}
+	private void addStartTagVariable(String tagToAdd,ITextRegionCollection customTag, int index){
+		IFile f = getFile();
+
+		if (f == null || !f.exists())
+			return;
+		TaglibHelper helper = TaglibHelperManager.getInstance().getTaglibHelper(f);
+		String decl = ""; //$NON-NLS-1$
+		List problems = new ArrayList();
+		CustomTag tag = helper.getCustomTag(tagToAdd, getStructuredDocument(), customTag, problems);
+		TaglibVariable[] taglibVars = tag.getTagVariables();
+		fTranslationProblems.addAll(problems);
+		/*
+		 * Add AT_BEGIN variables
+		 */
+		for (int i = 0; i < taglibVars.length; i++) {
+			if (taglibVars[i].getScope() == VariableInfo.AT_BEGIN) {
+				decl = taglibVars[i].getDeclarationString();
+				appendToBuffer(decl, fUserCode, false, customTag);
+			}
+		}
+		boolean isEmptyTag = false;
+		if (index != -1)
+			isEmptyTag= isEmptyTag(customTag, index);
+		else
+			isEmptyTag= isEmptyTag(customTag);
+		
+		/*
+		 * Add a single  { to limit the scope of NESTED variables
+		 */
+		StringBuffer text = new StringBuffer();
+		if (!isEmptyTag && tag.isIterationTag() && tag.getTagClassName() != null) {
+			text.append("\nwhile(true) "); //$NON-NLS-1$
+		}
+		text.append("{ // <"); //$NON-NLS-1$
+		text.append(tagToAdd);
+		if (isEmptyTag)
+			text.append("/>\n"); //$NON-NLS-1$
+		else
+			text.append(">\n"); //$NON-NLS-1$
+
+		appendToBuffer(text.toString(), fUserCode, false, customTag);
+
+		for (int i = 0; i < taglibVars.length; i++) {
+			if (taglibVars[i].getScope() == VariableInfo.NESTED) {
+				decl = taglibVars[i].getDeclarationString();
+				appendToBuffer(decl, fUserCode, false, customTag);
+			}
+		}
+		/*
+		 * For empty tags, add the corresponding } and AT_END variables immediately.  
+		 */
+		if (isEmptyTag) {
+			text = new StringBuffer();
+			text.append("} // <"); //$NON-NLS-1$
+			text.append(tagToAdd);
+			text.append("/>\n"); //$NON-NLS-1$
+			appendToBuffer(text.toString(), fUserCode, false, customTag);
+			/* Treat this as the end for empty tags */
+			for (int i = 0; i < taglibVars.length; i++) {
+				if (taglibVars[i].getScope() == VariableInfo.AT_END) {
+					decl = taglibVars[i].getDeclarationString();
+					appendToBuffer(decl, fUserCode, false, customTag);
+				}
+			}
+		}
+		else {
+			/*
+			 * For non-empty tags, remember the variable information
+			 */
+			fTagToVariableMap.push(tagToAdd, new RegionTags(customTag, tag));
+		}
+		
+	}
+
+	private boolean isEmptyTag(ITextRegionCollection customTag, int index) {
+		String type = null;
+		// custom tag is embedded
+		ITextRegionList regions = customTag.getRegions();
+		ITextRegion nextRegion = regions.get(index);
+		int size = customTag.getNumberOfRegions() ;
+		type = nextRegion.getType();
+		while (index <= size && !(DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(type) || DOMRegionContext.XML_TAG_NAME.equals(type) || DOMRegionContext.XML_TAG_CLOSE.equals(type) )) {
+				nextRegion = regions.get(++index);
+				type = nextRegion.getType();
+		}
+		
+		return DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(type);
+	}
+	
+	private boolean isEmptyTag(ITextRegionCollection customTag) {
+		ITextRegion lastRegion = customTag.getLastRegion();
+		// custom tag is embedded
+		if (customTag instanceof ITextRegionContainer) {
+			ITextRegionList regions = customTag.getRegions();
+			int size = customTag.getNumberOfRegions() - 1;
+			while (size > 0 && !(DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(lastRegion.getType()) || DOMRegionContext.XML_TAG_NAME.equals(lastRegion.getType()) || DOMRegionContext.XML_TAG_CLOSE.equals(lastRegion.getType()) )) {
+				lastRegion = regions.get(--size);
+			}
+		}
+		
+		return DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(lastRegion.getType());
+	}
+
+	private void addCustomTaglibVariables(String tagToAdd, ITextRegionCollection customTag, ITextRegion prevRegion, int index) {
+		//Can't judge by first region as start and end tag are part of same ContextRegionContainer		
+		if (prevRegion != null && prevRegion.getType().equals(DOMRegionContext.XML_END_TAG_OPEN)) {
+			/*
+			 * End tag
+			 */
+			addEndTagVariable(tagToAdd, customTag);
+		}
+		else if (prevRegion != null && prevRegion.getType().equals(DOMRegionContext.XML_TAG_OPEN)) {
+			/*
+			 * Start tag
+			 */
+			addStartTagVariable(tagToAdd,customTag, index);
+		}
+	}
+
+	private void doAfterBody(StringBuffer buffer, RegionTags regionTag) {
+		buffer.append("\tif ( (new "); //$NON-NLS-1$
+		buffer.append(regionTag.tag.getTagClassName());
+		buffer.append("()).doAfterBody() != javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_AGAIN)\n\t\tbreak;\n"); //$NON-NLS-1$
+	}
+
+	/**
+	 * @return the workspace file for this model, null otherwise
+	 */
+	private IFile getFile() {
+		IFile f = null;
+		ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(getStructuredDocument());
+		if (buffer != null) {
+			IPath path = buffer.getLocation();
+			if (path.segmentCount() > 1) {
+				f = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
+			}
+			if (f != null && f.isAccessible()) {
+				return f;
+			}
+		}
+		return null;
+	}
+
+	/*
+	 * used by inner helper class (XMLJSPRegionHelper, JSPIncludeRegionHelper)
+	 */
+	public List getBlockMarkers() {
+		if (fBlockMarkers == null)
+			fBlockMarkers = new ArrayList();
+		return fBlockMarkers;
+	}
+
+	/**
+	 * the main control loop for translating the document, driven by the
+	 * structuredDocument nodes
+	 */
+	public void translate() {
+		if (fTagToVariableMap == null) {
+			fTagToVariableMap = new StackMap();
+		}
+		fTranslationProblems.clear();
+
+		setCurrentNode(new ZeroStructuredDocumentRegion(fStructuredDocument, 0));
+		translatePreludes();
+
+		setCurrentNode(fStructuredDocument.getFirstStructuredDocumentRegion());
+
+		while (getCurrentNode() != null && !isCanceled()) {
+			//no code has been translated for this region yet
+			fCodeTranslated = false;
+			// intercept HTML comment flat node
+			// also handles UNDEFINED (which is what CDATA comes in as)
+			// basically this part will handle any "embedded" JSP containers
+			if (getCurrentNode().getType() == DOMRegionContext.XML_COMMENT_TEXT || getCurrentNode().getType() == DOMRegionContext.XML_CDATA_TEXT || getCurrentNode().getType() == DOMRegionContext.UNDEFINED) {
+				translateXMLCommentNode(getCurrentNode());
+			}
+			else {
+				// iterate through each region in the flat node
+				translateRegionContainer(getCurrentNode(), STANDARD_JSP);
+			}
+			
+			//if no code was translated for this region then found "non translated code"
+			if(!fCodeTranslated) {
+				fFoundNonTranslatedCode = true;
+			}
+			
+			if (getCurrentNode() != null)
+				advanceNextNode();
+		}
+		
+		writePlaceHolderForNonTranslatedCode();
+
+		setCurrentNode(new ZeroStructuredDocumentRegion(fStructuredDocument, fStructuredDocument.getLength()));
+		translateCodas();
+
+		/*
+		 * Any contents left in the map indicate start tags that never had end
+		 * tags. While the '{' that is present without the matching '}' should
+		 * cause a Java translation fault, that's not particularly helpful to
+		 * a user who may only know how to use custom tags as tags. Ultimately
+		 * unbalanced custom tags should just be reported as unbalanced tags,
+		 * and unbalanced '{'/'}' only reported when the user actually
+		 * unbalanced them with scriptlets.
+		 */
+		Iterator regionAndTaglibVariables = fTagToVariableMap.values().iterator();
+		while (regionAndTaglibVariables.hasNext()) {
+			RegionTags regionTag = (RegionTags) regionAndTaglibVariables.next();
+			ITextRegionCollection extraStartRegion = regionTag.region;
+			IJSPProblem missingEndTag = createJSPProblem(IJSPProblem.EndCustomTagMissing, IJSPProblem.F_PROBLEM_ID_LITERAL, NLS.bind(JSPCoreMessages.JSPTranslator_5,regionTag.tag.getTagName()), extraStartRegion.getStartOffset(), extraStartRegion.getEndOffset());
+			fTranslationProblems.add(missingEndTag);
+
+			StringBuffer text = new StringBuffer();
+			// Account for iteration tags that have a missing end tag
+			if (regionTag.tag.isIterationTag())
+				doAfterBody(text, regionTag);
+			text.append("} // [</"); //$NON-NLS-1$
+			text.append(regionTag.tag.getTagName());
+			text.append(">]"); //$NON-NLS-1$
+			appendToBuffer(text.toString(), fUserCode, false, fStructuredDocument.getLastStructuredDocumentRegion());
+		}
+		fTagToVariableMap.clear();
+
+		/*
+		 * Now do the same for jsp:useBean tags, whose contents get their own
+		 * { & }
+		 */
+		while (!fUseBeansStack.isEmpty()) {
+			appendToBuffer("}", fUserCode, false, fStructuredDocument.getLastStructuredDocumentRegion()); //$NON-NLS-1$
+			ITextRegionCollection extraStartRegion = (ITextRegionCollection) fUseBeansStack.pop();
+			IJSPProblem missingEndTag = createJSPProblem(IJSPProblem.UseBeanEndTagMissing, IJSPProblem.F_PROBLEM_ID_LITERAL, NLS.bind(JSPCoreMessages.JSPTranslator_5,JSP11Namespace.ElementName.USEBEAN), extraStartRegion.getStartOffset(), extraStartRegion.getEndOffset());
+			fTranslationProblems.add(missingEndTag);
+		}
+
+		buildResult(true);
+	}
+
+ 	/**
+	 * Translates a region container (and XML JSP container, or <% JSP
+	 * container). This method should only be called in this class and for
+	 * containers in the primary structured document as all buffer appends
+	 * will be direct.
+	 */
+	protected void setDocumentContent(IDocument document, InputStream contentStream, String charset) {
+		Reader in = null;
+		try {
+			in = new BufferedReader(new InputStreamReader(contentStream, charset), 2048);
+			StringBuffer buffer = new StringBuffer(2048);
+			char[] readBuffer = new char[2048];
+			int n = in.read(readBuffer);
+			while (n > 0) {
+				buffer.append(readBuffer, 0, n);
+				n = in.read(readBuffer);
+			}
+			document.set(buffer.toString());
+		}
+		catch (IOException x) {
+			// ignore
+		}
+		finally {
+			if (in != null) {
+				try {
+					in.close();
+				}
+				catch (IOException x) {
+					// ignore
+				}
+			}
+		}
+	}
+
+	protected void init() {
+		fClassname = "_JSPServlet"; //$NON-NLS-1$
+		fClassHeader = "public class " + fClassname + " extends "; //$NON-NLS-1$ //$NON-NLS-2$
+		
+		fImplicitImports = "import javax.servlet.*;" + ENDL + //$NON-NLS-1$
+					"import javax.servlet.http.*;" + ENDL + //$NON-NLS-1$
+					"import javax.servlet.jsp.*;" + ENDL + ENDL; //$NON-NLS-1$
+
+		fServiceHeader = "public void _jspService(javax.servlet.http.HttpServletRequest request," + //$NON-NLS-1$
+					" javax.servlet.http.HttpServletResponse response)" + ENDL + //$NON-NLS-1$
+					"\t\tthrows java.io.IOException, javax.servlet.ServletException {" + ENDL + //$NON-NLS-1$
+					"javax.servlet.jsp.PageContext pageContext = JspFactory.getDefaultFactory().getPageContext(this, request, response, null, true, JspWriter.DEFAULT_BUFFER, true);" + ENDL + //$NON-NLS-1$
+					"javax.servlet.ServletContext application = pageContext.getServletContext();" + ENDL + //$NON-NLS-1$
+					"javax.servlet.ServletConfig config = pageContext.getServletConfig();" + ENDL + //$NON-NLS-1$ 
+					"javax.servlet.jsp.JspWriter out = pageContext.getOut();" + ENDL + //$NON-NLS-1$
+					"Object page = this;" + ENDL; //$NON-NLS-1$
+		fSuperclass = "javax.servlet.http.HttpServlet"; //$NON-NLS-1$
+	}
+
+	/**
+	 * 
+	 * @return the status of the translator's progrss monitor, false if the
+	 *         monitor is null
+	 */
+	private boolean isCanceled() {
+		return (fProgressMonitor == null) ? false : fProgressMonitor.isCanceled();
+	}
+
+	private void advanceNextNode() {
+		setCurrentNode(getCurrentNode().getNext());
+		if (getCurrentNode() != null)
+			setSourceReferencePoint();
+	}
+
+	private void setSourceReferencePoint() {
+		if (isJSP(getCurrentNode().getFirstRegion().getType())) {
+			Iterator it = getCurrentNode().getRegions().iterator();
+			ITextRegion r = null;
+			while (it.hasNext()) {
+				r = (ITextRegion) it.next();
+				if (r.getType() == DOMJSPRegionContexts.JSP_CONTENT || r.getType() == DOMRegionContext.XML_CONTENT)
+					break;
+				else if (r.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME)
+					break;
+				else if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE && getCurrentNode().getFullText(r).trim().equals("import")) //$NON-NLS-1$
+					break;
+			}
+		}
+	}
+
+	/**
+	 * translates a region container (and XML JSP container, or <% JSP
+	 * container)
+	 * 
+	 * This method should only be called in this class and for containers in
+	 * the primary structured document as all buffer appends will be direct
+	 */
+	protected void translateRegionContainer(ITextRegionCollection container, int JSPType) {
+
+		ITextRegionCollection containerRegion = container;
+
+		Iterator regions = containerRegion.getRegions().iterator();
+		ITextRegion region = null;
+		while (regions.hasNext()) {
+			region = (ITextRegion) regions.next();
+			String type = region.getType();
+
+			// content assist was not showing up in JSP inside a javascript region
+			if (DOMRegionContext.BLOCK_TEXT == type) {
+				// check if it's nested jsp in a script tag...
+				if (region instanceof ITextRegionContainer) {
+					// pass in block text's container & iterator
+					Iterator regionIterator = ((ITextRegionCollection) region).getRegions().iterator();
+					translateJSPNode(region, regionIterator, type, EMBEDDED_JSP);
+				}
+				else {
+					//be sure to combine all of the text from the block region
+					StringBuffer fullText = new StringBuffer(containerRegion.getFullText(region));
+					while(regions.hasNext()) {
+						region = (ITextRegion)regions.next();
+						if (region instanceof ITextRegionContainer) {
+							// pass in block text's container & iterator
+							Iterator regionIterator = ((ITextRegionCollection) region).getRegions().iterator();
+							translateJSPNode(region, regionIterator, type, EMBEDDED_JSP);
+						}
+						
+						if(region.getType() == DOMRegionContext.BLOCK_TEXT) {
+							fullText.append(containerRegion.getFullText(region));
+						} else {
+							//update type for when we exit if statement for BLOCK_TEXT
+							type = region.getType();
+							break;
+						}
+					}
+					
+					/**
+					 * LIMITATION - Normally the script content within a
+					 * script tag is a single document region with a single
+					 * BLOCK_TEXT text region within it. Any JSP scripting
+					 * will be within its own region container (for the sake
+					 * of keeping the scripting open/content/end as a group)
+					 * also of BLOCK_TEXT. That ignores custom tags that might
+					 * be in there, though, as they require proper scoping and
+					 * variable declaration to be performed even though
+					 * they're not proper nodes in the DOM. The only way to
+					 * really do this is to treat the entire script content as
+					 * JSP content on its own, akin to an included segment.
+					 * Further complicating this solution is that tagdependent
+					 * custom tags have their comment marked as BLOCK_TEXT as
+					 * well, so there's no clear way to tell the two cases
+					 * apart.
+					 */
+
+					// ////////////////////////////////////////////////////////////////////////////////
+					// THIS EMBEDDED JSP TEXT WILL COME OUT LATER WHEN
+					// PARTITIONING HAS
+					// SUPPORT FOR NESTED XML-JSP
+					// CMVC 241882
+					decodeScriptBlock(fullText.toString(), containerRegion.getStartOffset());
+					// ////////////////////////////////////////////////////////////////////////////////
+				}
+			}
+			// if (region instanceof ITextRegionCollection &&
+			// ((ITextRegionCollection) region).getNumberOfRegions() > 0) {
+			// translateRegionContainer((ITextRegionCollection) region,
+			// EMBEDDED_JSP);
+			// }
+			if (type != null && isJSP(type)) // <%, <%=, <%!, <%@
+			{
+				// translateJSPNode(region, regions, type, JSPType);
+				translateJSPNode(containerRegion, regions, type, JSPType);
+			}
+			else if (type != null && (type == DOMRegionContext.XML_TAG_OPEN || type == DOMRegionContext.XML_END_TAG_OPEN)) {
+				translateXMLNode(containerRegion, regions);
+			}
+			else if(type != null && type == DOMRegionContext.XML_CONTENT && region instanceof ITextRegionContainer) {
+				//this case was put in to parse EL that is not in an attribute
+				translateXMLContent((ITextRegionContainer)region);
+			}
+			//the end tags of these regions are "translated" in a sense
+			else if(type == DOMJSPRegionContexts.JSP_DIRECTIVE_CLOSE ||
+					type == DOMJSPRegionContexts.JSP_CLOSE) {
+				this.fCodeTranslated = true;
+			}
+		}
+	}
+
+	/*
+	 * ////////////////////////////////////////////////////////////////////////////////// **
+	 * TEMP WORKAROUND FOR CMVC 241882 Takes a String and blocks out
+	 * jsp:scriptlet, jsp:expression, and jsp:declaration @param blockText
+	 * @return
+	 */
+	void decodeScriptBlock(String blockText, int startOfBlock) {
+		XMLJSPRegionHelper helper = new XMLJSPRegionHelper(this, false);
+		helper.addBlockMarker(new BlockMarker("jsp:scriptlet", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
+		helper.addBlockMarker(new BlockMarker("jsp:expression", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
+		helper.addBlockMarker(new BlockMarker("jsp:declaration", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
+		helper.addBlockMarker(new BlockMarker("jsp:directive.include", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
+		helper.addBlockMarker(new BlockMarker("jsp:directive.taglib", null, DOMJSPRegionContexts.JSP_CONTENT, false)); //$NON-NLS-1$
+		helper.reset(blockText, startOfBlock);
+		// force parse
+		helper.forceParse();
+	}
+
+	/*
+	 * returns string minus CDATA open and close text
+	 */
+	final public String stripCDATA(String text) {
+		String resultText = ""; //$NON-NLS-1$
+		String CDATA_OPEN = "<![CDATA["; //$NON-NLS-1$
+		String CDATA_CLOSE = "]]>"; //$NON-NLS-1$
+		int start = 0;
+		int end = text.length();
+		while (start < text.length()) {
+			if (text.indexOf(CDATA_OPEN, start) > -1) {
+				end = text.indexOf(CDATA_OPEN, start);
+				resultText += text.substring(start, end);
+				start = end + CDATA_OPEN.length();
+			}
+			else if (text.indexOf(CDATA_CLOSE, start) > -1) {
+				end = text.indexOf(CDATA_CLOSE, start);
+				resultText += text.substring(start, end);
+				start = end + CDATA_CLOSE.length();
+			}
+			else {
+				end = text.length();
+				resultText += text.substring(start, end);
+				break;
+			}
+		}
+		return resultText;
+	}
+
+	// END OF WORKAROUND CODE...
+	// /////////////////////////////////////////////////////////////////////////////////////
+	/**
+	 * determines if the type is a pure JSP type (not XML)
+	 */
+	protected boolean isJSP(String type) {
+		return ((type == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN || type == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN || type == DOMJSPRegionContexts.JSP_DECLARATION_OPEN || type == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN || type == DOMJSPRegionContexts.JSP_CONTENT || type == DOMJSPRegionContexts.JSP_EL_OPEN) && type != DOMRegionContext.XML_TAG_OPEN);
+		// checking XML_TAG_OPEN so <jsp:directive.xxx/> gets treated like
+		// other XML jsp tags
+	}
+
+	/**
+	 * This currently only detects EL content and translates it,
+	 * but if other cases arise later then they could be added in here
+	 * 
+	 * @param embeddedContainer the container that may contain EL
+	 */
+	protected void translateXMLContent(ITextRegionContainer embeddedContainer) {
+		ITextRegionList embeddedRegions = embeddedContainer.getRegions();
+		int length = embeddedRegions.size();
+		for (int i = 0; i < length; i++) {
+			ITextRegion delim = embeddedRegions.get(i);
+			String type = delim.getType();
+
+			// check next region to see if it's EL content
+			if (i + 1 < length) {
+				if((type == DOMJSPRegionContexts.JSP_EL_OPEN || type == DOMJSPRegionContexts.JSP_VBL_OPEN)) {
+					ITextRegion region = null;
+					
+					int start = delim.getEnd();
+					while (++i < length) {
+						region = embeddedRegions.get(i);
+						if (region == null || !isELType(region.getType()))
+							break;
+					}
+					fLastJSPType = EXPRESSION;
+					String elText = embeddedContainer.getFullText().substring(start, (region != null ? region.getStart() : embeddedContainer.getLength() - 1));
+					translateEL(elText, embeddedContainer.getText(delim), fCurrentNode,
+							embeddedContainer.getEndOffset(delim), elText.length());
+				}
+			}
+		}
+	}
+
+	private boolean isELType(String type) {
+		return DOMJSPRegionContexts.JSP_EL_CONTENT.equals(type) || DOMJSPRegionContexts.JSP_VBL_CONTENT.equals(type) || DOMJSPRegionContexts.JSP_EL_DQUOTE.equals(type) || DOMJSPRegionContexts.JSP_VBL_DQUOTE.equals(type) || DOMJSPRegionContexts.JSP_EL_QUOTED_CONTENT.equals(type) || DOMJSPRegionContexts.JSP_VBL_QUOTED_CONTENT.equals(type) || DOMJSPRegionContexts.JSP_EL_SQUOTE.equals(type) || DOMJSPRegionContexts.JSP_VBL_SQUOTE.equals(type);
+	}
+
+	/**
+	 * translates the various XMLJSP type nodes
+	 * 
+	 * @param regions
+	 *            the regions of the XMLNode
+	 */
+	protected void translateXMLNode(ITextRegionCollection container, Iterator regions) {
+		// contents must be valid XHTML, translate escaped CDATA into what it
+		// really is...
+		ITextRegion r = null;
+		if (regions.hasNext()) {
+			r = (ITextRegion) regions.next();
+			// <jsp:directive.xxx > comes in as this
+			if (r.getType() == DOMRegionContext.XML_TAG_NAME || r.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME)
+
+			{
+				String fullTagName = container.getText(r);
+				if (fullTagName.indexOf(':') > -1 && !fullTagName.startsWith(JSP_PREFIX)) {
+					addTaglibVariables(fullTagName, container,-1); // it
+					// may
+					// be a
+					// custom
+					// tag
+				}
+				StringTokenizer st = new StringTokenizer(fullTagName, ":.", false); //$NON-NLS-1$
+				if (st.hasMoreTokens() && st.nextToken().equals("jsp")) //$NON-NLS-1$
+				{
+					if (st.hasMoreTokens()) {
+						String jspTagName = st.nextToken();
+
+						if (jspTagName.equals("scriptlet")) //$NON-NLS-1$
+						{
+							translateXMLJSPContent(SCRIPTLET);
+						}
+						else if (jspTagName.equals("expression")) //$NON-NLS-1$
+						{
+							translateXMLJSPContent(EXPRESSION);
+						}
+						else if (jspTagName.equals("declaration")) //$NON-NLS-1$
+						{
+							translateXMLJSPContent(DECLARATION);
+						}
+						else if (jspTagName.equals("directive")) //$NON-NLS-1$
+						{
+							if (st.hasMoreTokens()) {
+								String directiveName = st.nextToken();
+								if (directiveName.equals("taglib")) { //$NON-NLS-1$
+									while (r != null && regions.hasNext() && !r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
+										r = (ITextRegion) regions.next();
+										if (container.getText(r).equals(JSP11Namespace.ATTR_NAME_PREFIX)) {
+											String prefix = getAttributeValue(r, regions);
+											if (prefix != null) {
+												handleTaglib(prefix);
+											}
+										}
+									}
+									return;
+								}
+								else if (directiveName.equals("include")) { //$NON-NLS-1$
+
+									String fileLocation = ""; //$NON-NLS-1$
+
+									// skip to required "file" attribute,
+									// should be safe because
+									// "file" is the only attribute for the
+									// include directive
+									while (r != null && regions.hasNext() && !r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
+										r = (ITextRegion) regions.next();
+									}
+									fileLocation = getAttributeValue(r, regions);
+									if (fileLocation != null)
+										handleIncludeFile(fileLocation);
+								}
+								else if (directiveName.equals("page")) { //$NON-NLS-1$
+
+									// bad if currentNode is referenced after
+									// here w/ the current list
+									// see:
+									// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3035
+									// setCurrentNode(getCurrentNode().getNext());
+									if (getCurrentNode() != null) {
+										// 'regions' contain the attrs
+										translatePageDirectiveAttributes(regions, getCurrentNode());
+									}
+								}
+								else if (directiveName.equals("tag")) { //$NON-NLS-1$
+									translatePageDirectiveAttributes(regions, getCurrentNode());
+								}
+								else if (directiveName.equals("variable")) { //$NON-NLS-1$
+									translateVariableDirectiveAttributes(regions);
+								}
+							}
+						}
+						else if (jspTagName.equals("include")) { //$NON-NLS-1$
+							// <jsp:include page="filename") />
+							checkAttributeValueContainer(regions, "page"); //$NON-NLS-1$
+						}
+						else if (jspTagName.equals("forward")) { //$NON-NLS-1$
+							checkAttributeValueContainer(regions, "page"); //$NON-NLS-1$
+						}
+						else if (jspTagName.equals("param")) { //$NON-NLS-1$
+							checkAttributeValueContainer(regions, "value"); //$NON-NLS-1$
+						}
+						else if (jspTagName.equals("setProperty")) { //$NON-NLS-1$
+							checkAttributeValueContainer(regions, "value"); //$NON-NLS-1$
+						}
+						else if (jspTagName.equals("useBean")) //$NON-NLS-1$
+						{
+							checkAttributeValueContainer(regions, "name"); //$NON-NLS-1$
+							// https://bugs.eclipse.org/bugs/show_bug.cgi?id=103004
+							// advanceNextNode(); // get the content
+							if (getCurrentNode() != null) {
+								translateUseBean(container); // 'regions'
+							}
+						}
+
+					}
+				}
+				else {
+					checkAllAttributeValueContainers(container,regions);
+				}
+			}
+		}
+	}
+
+	/**
+	 * translates embedded containers for ALL attribute values
+	 * 
+	 * @param regions
+	 */
+	private void checkAllAttributeValueContainers(ITextRegionCollection container, Iterator regions) {
+		// tag name is not jsp
+		// handle embedded jsp attributes...
+		ITextRegion embedded = null;
+		// Iterator attrRegions = null;
+		// ITextRegion attrChunk = null;
+		ITextRegion prevRegion = null;
+		while (regions.hasNext()) {
+			embedded = (ITextRegion) regions.next();
+			if (embedded.getType() == DOMRegionContext.XML_TAG_NAME || embedded.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME)
+
+			{
+				String fullTagName = container.getText(embedded);
+				if (fullTagName.indexOf(':') > -1 && !fullTagName.startsWith(JSP_PREFIX)) {
+					if (prevRegion != null)
+					addCustomTaglibVariables(fullTagName, container,prevRegion,-1); // it may be a custom tag
+				}
+			}
+				else if (embedded instanceof ITextRegionContainer) {
+				// parse out container
+
+				// https://bugs.eclipse.org/bugs/show_bug.cgi?id=130606
+				// fix exponential iteration problem w/ embedded expressions
+				translateEmbeddedJSPInAttribute((ITextRegionContainer) embedded);
+				// attrRegions = ((ITextRegionContainer)
+				// embedded).getRegions().iterator();
+				// while (attrRegions.hasNext()) {
+				// attrChunk = (ITextRegion) attrRegions.next();
+				// String type = attrChunk.getType();
+				// // embedded JSP in attribute support only want to
+				// // translate one time per
+				// // embedded region so we only translate on the JSP open
+				// // tags (not content)
+				// if (type == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN ||
+				// type ==
+				// DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN || type ==
+				// DOMJSPRegionContexts.JSP_DECLARATION_OPEN || type ==
+				// DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN || type ==
+				// DOMJSPRegionContexts.JSP_EL_OPEN) {
+				// // now call jsptranslate
+				// translateEmbeddedJSPInAttribute((ITextRegionContainer)
+				// embedded);
+				// break;
+				// }
+				// }
+			}
+			prevRegion = embedded;
+		}
+	}
+
+	/**
+	 * translates embedded container for specified attribute
+	 * 
+	 * @param regions
+	 * @param attrName
+	 */
+	private void checkAttributeValueContainer(Iterator regions, String attrName) {
+		ITextRegion r = null;
+		while (regions.hasNext()) {
+			r = (ITextRegion) regions.next();
+			if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME && getCurrentNode().getText(r).equals(attrName)) {
+				// skip to attribute value
+				while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null) {
+					if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)
+						break;
+				}
+				// forces embedded region to be translated
+				if (r instanceof ContextRegionContainer) {
+					translateEmbeddedJSPInAttribute((ContextRegionContainer) r);
+				}
+				break;
+			}
+		}
+	}
+
+	/*
+	 * example:
+	 * 
+	 * <jsp:scriptlet>scriptlet jsp-java content <![CDATA[ more jsp java ]]>
+	 * jsp-java content... <![CDATA[ more jsp java ]]> </jsp:scriptlet>
+	 * 
+	 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=93366
+	 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=88590 translate
+	 * everything inbetween <scriptlet> tags, which may be more than one
+	 * region (esp. CDATA)
+	 * 
+	 */
+	private void translateXMLJSPContent(int type) {
+
+		IStructuredDocumentRegion sdr = getCurrentNode().getNext();
+		int start = sdr.getStartOffset();
+		int end = sdr.getEndOffset();
+		String sdrText = ""; //$NON-NLS-1$
+
+		StringBuffer regionText = new StringBuffer();
+		// read structured document regions until </jsp:scriptlet> or EOF
+		while (sdr != null && sdr.getType() != DOMRegionContext.XML_TAG_NAME) {
+
+			// setup for next region
+			if (regionText.length() == 0)
+				start = sdr.getStartOffset();
+			sdrText = sdr.getText();
+
+			if (sdr.getType() == DOMRegionContext.XML_CDATA_TEXT) {
+				// Clear out the buffer
+				if (regionText.length() > 0) {
+					writeToBuffer(type, regionText.toString(), start, end);
+					regionText = new StringBuffer();
+				}
+				// just to be safe, make sure CDATA start & end are there
+				if (sdrText.startsWith("<![CDATA[") && sdrText.endsWith("]]>")) { //$NON-NLS-1$ //$NON-NLS-2$
+
+					start = sdr.getStartOffset() + 9; // <![CDATA[
+					end = sdr.getEndOffset() - 3; // ]]>
+					sdrText = sdrText.substring(9, sdrText.length() - 3);
+					writeToBuffer(type, sdrText, start, end);
+				}
+			}
+			else {
+				// handle entity references
+				regionText.append(EscapedTextUtil.getUnescapedText(sdrText));
+				end = sdr.getEndOffset();
+			}
+			sdr = sdr.getNext();
+		}
+
+		if (regionText.length() > 0)
+			writeToBuffer(type, regionText.toString(), start, end);
+		setCurrentNode(sdr);
+		setSourceReferencePoint();
+	}
+
+	private void writeToBuffer(int type, String content, int jspStart, int jspEnd) {
+		switch (type) {
+			case SCRIPTLET :
+				translateScriptletString(content, getCurrentNode(), jspStart, jspEnd - jspStart, false);
+				break;
+			case EXPRESSION :
+				translateExpressionString(content, getCurrentNode(), jspStart, jspEnd - jspStart, false);
+				break;
+			case DECLARATION :
+				translateDeclarationString(content, getCurrentNode(), jspStart, jspEnd - jspStart, false);
+				break;
+		}
+	}
+
+	/**
+	 * goes through comment regions, checks if any are an embedded JSP
+	 * container if it finds one, it's sends the container into the
+	 * translation routine
+	 */
+	protected void translateXMLCommentNode(IStructuredDocumentRegion node) {
+		Iterator it = node.getRegions().iterator();
+		ITextRegion commentRegion = null;
+		while (it != null && it.hasNext()) {
+			commentRegion = (ITextRegion) it.next();
+			if (commentRegion instanceof ITextRegionContainer) {
+				translateRegionContainer((ITextRegionContainer) commentRegion, EMBEDDED_JSP); // it's
+				// embedded
+				// jsp...iterate
+				// regions...
+			}
+			else if (DOMRegionContext.XML_COMMENT_TEXT.equals(commentRegion.getType())) {
+				// https://bugs.eclipse.org/bugs/show_bug.cgi?id=222215
+				// support custom tags hidden in a comment region
+				decodeScriptBlock(node.getFullText(commentRegion), node.getStartOffset(commentRegion));
+			}
+		}
+	}
+
+	/**
+	 * determines which type of JSP node to translate
+	 */
+	protected void translateJSPNode(ITextRegion region, Iterator regions, String type, int JSPType) {
+		if (type == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN && regions != null) {
+			translateDirective(regions);
+		}
+		else {
+			ITextRegionCollection contentRegion = null;
+			if (JSPType == STANDARD_JSP && (setCurrentNode(getCurrentNode().getNext())) != null) {
+				contentRegion = getCurrentNode();
+			}
+			else if (JSPType == EMBEDDED_JSP && region instanceof ITextRegionCollection) {
+				translateEmbeddedJSPInBlock((ITextRegionCollection) region, regions);
+				// ensure the rest of this method won't be called
+			}
+			/* NOTE: the type here is of the node preceding the current node
+			 * thus must check to see if the current node is JSP close, if it is
+			 * then the JSP is something akin to <%%> and should not be translated
+			 * (Bug 189318)
+			 */
+			if (contentRegion != null && contentRegion.getType() != DOMJSPRegionContexts.JSP_CLOSE) {
+				if (type == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN) {
+					translateExpression(contentRegion);
+				}
+				else if (type == DOMJSPRegionContexts.JSP_DECLARATION_OPEN) {
+					translateDeclaration(contentRegion);
+				}
+				else if (type == DOMJSPRegionContexts.JSP_CONTENT || type == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN) {
+					translateScriptlet(contentRegion);
+				}
+			}
+			else {
+				// this is the case of an attribute w/ no region <p
+				// align="<%%>">
+				setCursorOwner(getJSPTypeForRegion(region));
+			}
+		}
+	}
+
+
+	private void translateEL(String elText, String delim, IStructuredDocumentRegion currentNode, int contentStart, int contentLength) {
+		IJSPELTranslator translator = getELTranslator();
+		if (null != translator) {
+			List elProblems = translator.translateEL(elText, delim, currentNode, contentStart, contentLength, fUserELExpressions, fUserELRanges, fStructuredDocument);
+			fTranslationProblems.addAll(elProblems);
+		}
+	}
+
+	/**
+	 * Discover and instantiate an EL translator.
+	 */
+	public IJSPELTranslator getELTranslator() {
+		if (fELTranslator == null) {
+
+			/*
+			 * name of plugin that exposes this extension point
+			 */
+			IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(JSP_CORE_PLUGIN_ID, EL_TRANSLATOR_EXTENSION_NAME); // -
+			// extension
+			// id
+
+			// Iterate over all declared extensions of this extension point.
+			// A single plugin may extend the extension point more than once,
+			// although it's not recommended.
+			IConfigurationElement bestTranslator = null;
+			IExtension[] extensions = extensionPoint.getExtensions();
+			for (int curExtension = 0; curExtension < extensions.length; curExtension++) {
+				IExtension extension = extensions[curExtension];
+
+				IConfigurationElement[] translators = extension.getConfigurationElements();
+				for (int curTranslator = 0; curTranslator < translators.length; curTranslator++) {
+
+					IConfigurationElement elTranslator = translators[curTranslator];
+
+					if (!EL_TRANSLATOR_EXTENSION_NAME.equals(elTranslator.getName())) { // -
+						// name
+						// of
+						// configElement
+						continue;
+					}
+
+					String idString = elTranslator.getAttribute("id"); //$NON-NLS-1$
+					if (null != idString && idString.equals(fELTranslatorID) || (null == bestTranslator && DEFAULT_JSP_EL_TRANSLATOR_ID.equals(idString))) {
+						bestTranslator = elTranslator;
+					}
+				}
+			}
+
+			if (null != bestTranslator) {
+				try {
+					Object execExt = bestTranslator.createExecutableExtension("class"); //$NON-NLS-1$
+					if (execExt instanceof IJSPELTranslator) {
+						return fELTranslator = (IJSPELTranslator) execExt;
+					}
+				}
+				catch (CoreException e) {
+					Logger.logException(e);
+				}
+			}
+		}
+		return fELTranslator;
+	}
+
+	/**
+	 * Pass the ITextRegionCollection which is the embedded region
+	 * 
+	 * @param regions
+	 *            iterator for collection
+	 */
+	private void translateEmbeddedJSPInBlock(ITextRegionCollection collection, Iterator regions) {
+		ITextRegion region = null;
+		while (regions.hasNext()) {
+			region = (ITextRegion) regions.next();
+			if (isJSP(region.getType()))
+				break;
+			region = null;
+		}
+		if (region != null) {
+			translateEmbeddedJSPInAttribute(collection);
+			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=126377
+			// all of collection was translated so just finish off iterator
+			while (regions.hasNext())
+				regions.next();
+		}
+	}
+
+	/*
+	 * Translates all embedded jsp regions in embeddedContainer for example:
+	 * <a href="index.jsp?p=<%=abc%>b=<%=xyz%>">abc</a>
+	 */
+	private void translateEmbeddedJSPInAttribute(ITextRegionCollection embeddedContainer) {
+		// THIS METHOD IS A FIX FOR
+		// jsp embedded in attribute regions
+		// loop all regions
+		ITextRegionList embeddedRegions = embeddedContainer.getRegions();
+		ITextRegion delim = null;
+		ITextRegion content = null;
+		String type = null;
+		String quotetype = null;
+		for (int i = 0; i < embeddedRegions.size(); i++) {
+
+			// possible delimiter, check later
+			delim = embeddedRegions.get(i);
+			type = delim.getType();
+			if (type == DOMRegionContext.XML_TAG_NAME ) {
+				String fullTagName = embeddedContainer.getText(delim);
+				if (fullTagName.indexOf(':') > -1 && !fullTagName.startsWith(JSP_PREFIX)) {
+					ITextRegion prevRegion =null;
+					if (i>0)
+						prevRegion = embeddedRegions.get(i-1);
+					addCustomTaglibVariables(fullTagName, embeddedContainer,prevRegion,i+1); // it may be a custom tag
+				}
+			}
+			if(type == DOMJSPRegionContexts.XML_TAG_ATTRIBUTE_VALUE_DQUOTE || type == DOMJSPRegionContexts.XML_TAG_ATTRIBUTE_VALUE_SQUOTE
+				|| type == DOMJSPRegionContexts.JSP_TAG_ATTRIBUTE_VALUE_DQUOTE || type == DOMJSPRegionContexts.JSP_TAG_ATTRIBUTE_VALUE_SQUOTE)
+				quotetype = type;
+
+			// check next region to see if it's content
+			if (i + 1 < embeddedRegions.size()) {
+				String regionType = embeddedRegions.get(i + 1).getType();
+				if (regionType == DOMJSPRegionContexts.JSP_CONTENT || regionType == DOMJSPRegionContexts.JSP_EL_CONTENT)
+					content = embeddedRegions.get(i + 1);
+			}
+
+			if (content != null) {
+				int contentStart = embeddedContainer.getStartOffset(content);
+				int rStart = fCurrentNode.getStartOffset() + contentStart;
+				int rEnd = fCurrentNode.getStartOffset() + embeddedContainer.getEndOffset(content);
+
+				boolean inThisRegion = rStart <= fSourcePosition && rEnd >= fSourcePosition;
+				// int jspPositionStart = fCurrentNode.getStartOffset() +
+				// contentStart;
+
+				if (type == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN) {
+					fLastJSPType = EXPRESSION;
+					// translateExpressionString(embeddedContainer.getText(content),
+					// fCurrentNode, contentStart, content.getLength());
+					translateExpressionString(embeddedContainer.getText(content), embeddedContainer, contentStart, content.getLength(), quotetype);
+				}
+				else if (type == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN) {
+					fLastJSPType = SCRIPTLET;
+					// translateScriptletString(embeddedContainer.getText(content),
+					// fCurrentNode, contentStart, content.getLength());
+					translateScriptletString(embeddedContainer.getText(content), embeddedContainer, contentStart, content.getLength(), false);
+				}
+				else if (type == DOMJSPRegionContexts.JSP_DECLARATION_OPEN) {
+					fLastJSPType = DECLARATION;
+					// translateDeclarationString(embeddedContainer.getText(content),
+					// fCurrentNode, contentStart, content.getLength());
+					translateDeclarationString(embeddedContainer.getText(content), embeddedContainer, contentStart, content.getLength(), false);
+				}
+				else if (type == DOMJSPRegionContexts.JSP_EL_OPEN) {
+					fLastJSPType = EXPRESSION;
+					translateEL(embeddedContainer.getText(content), embeddedContainer.getText(delim), fCurrentNode, contentStart, content.getLength());
+				}
+
+				// calculate relative offset in buffer
+				if (inThisRegion) {
+					setCursorOwner(fLastJSPType);
+					int currentBufferLength = getCursorOwner().length();
+					setRelativeOffset((fSourcePosition - contentStart) + currentBufferLength);
+					if (fLastJSPType == EXPRESSION) {
+						// if an expression, add then length of the enclosing
+						// paren..
+						setCursorInExpression(true);
+						setRelativeOffset(getRelativeOffset() + EXPRESSION_PREFIX.length());
+					}
+				}
+			}
+			else {
+				type = null;
+			}
+		}
+	}
+
+	private int fLastJSPType = SCRIPTLET;
+
+	/**
+	 * JSPType is only used internally in this class to describe tye type of
+	 * region to be translated
+	 * 
+	 * @param region
+	 * @return int
+	 */
+	private int getJSPTypeForRegion(ITextRegion region) {
+		String regionType = region.getType();
+		int type = SCRIPTLET;
+		if (regionType == DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN)
+			type = SCRIPTLET;
+		else if (regionType == DOMJSPRegionContexts.JSP_EXPRESSION_OPEN)
+			type = EXPRESSION;
+		else if (regionType == DOMJSPRegionContexts.JSP_DECLARATION_OPEN)
+			type = DECLARATION;
+		else if (regionType == DOMJSPRegionContexts.JSP_CONTENT)
+			type = fLastJSPType;
+		// remember the last type, in case the next type that comes in is
+		// JSP_CONTENT
+		fLastJSPType = type;
+		return type;
+	}
+
+	/**
+	 * /* <%@ %> /* need to pass in the directive tag region
+	 */
+	protected void translateDirective(Iterator regions) {
+		ITextRegion r = null;
+		String regionText, attrValue = ""; //$NON-NLS-1$
+		while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME) { // could
+			// be
+			// XML_CONTENT
+			// =
+			// "",
+			// skips
+			// attrs?
+			regionText = getCurrentNode().getText(r);
+			if (regionText.equals("taglib")) { //$NON-NLS-1$
+				// add custom tag block markers here
+				handleTaglib();
+				return;
+			}
+			else if (regionText.equals("include")) { //$NON-NLS-1$
+				String fileLocation = ""; //$NON-NLS-1$
+				// CMVC 258311
+				// PMR 18368, B663
+				// skip to required "file" attribute, should be safe because
+				// "file" is the only attribute for the include directive
+				while (r != null && regions.hasNext() && !r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
+					r = (ITextRegion) regions.next();
+				}
+				fileLocation = getAttributeValue(r, regions);
+				if (attrValue != null)
+					handleIncludeFile(fileLocation);
+			}
+			else if (regionText.equals("page")) { //$NON-NLS-1$
+				translatePageDirectiveAttributes(regions, getCurrentNode());
+			}
+			else if (regionText.equals("tag")) { //$NON-NLS-1$
+				// some attributes overlap, so both are handled in this method
+				translatePageDirectiveAttributes(regions, getCurrentNode());
+			}
+			else if (regionText.equals("variable")) { //$NON-NLS-1$
+				translateVariableDirectiveAttributes(regions);
+			}
+			else if (regionText.equals("attribute")) { //$NON-NLS-1$
+				translateAttributeDirectiveAttributes(regions);
+			}
+		}
+	}
+
+	private void translateAttributeDirectiveAttributes(Iterator regions) {
+		ITextRegion r = null;
+		String attrName, attrValue;
+
+		String varType = "java.lang.String"; //$NON-NLS-1$ // the default class...
+		String varName = null;
+		String description = "";//$NON-NLS-1$ 
+		boolean isFragment = false;
+
+		// iterate all attributes
+		while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() != DOMJSPRegionContexts.JSP_CLOSE) {
+			attrName = attrValue = null;
+			if (r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
+				attrName = getCurrentNode().getText(r).trim();
+				if (attrName.length() > 0) {
+					if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
+						if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
+							attrValue = StringUtils.strip(getCurrentNode().getText(r));
+						}
+						// has equals, but no value?
+					}
+					if (attrName.equals(JSP11Namespace.ATTR_NAME_TYPE)) {
+						varType = attrValue;
+					}
+					else if (attrName.equals(JSP20Namespace.ATTR_NAME_FRAGMENT)) {
+						isFragment = Boolean.valueOf(attrValue).booleanValue();
+					}
+					else if (attrName.equals(JSP11Namespace.ATTR_NAME_NAME)) {
+						varName = attrValue;
+					}
+					else if (attrName.equals(JSP20Namespace.ATTR_NAME_DESCRIPTION)) {
+						description = attrValue;
+					}
+				}
+			}
+		}
+		if (varName != null) {
+			if (isFragment) {
+				// 2.0:JSP.8.5.2
+				varType = "javax.servlet.jsp.tagext.JspFragment"; //$NON-NLS-1$
+			}
+			String declaration = new TaglibVariable(varType, varName, "", description).getDeclarationString(true, TaglibVariable.M_PRIVATE); //$NON-NLS-1$
+			appendToBuffer(declaration, fUserDeclarations, false, fCurrentNode);
+		}
+	}
+
+	private void translateVariableDirectiveAttributes(Iterator regions) {
+		/*
+		 * Shouldn't create a scripting variable in *this* tag file's
+		 * translation, only in JSP files that use it -
+		 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=188780
+		 */
+	}
+
+	/*
+	 * This method should ideally only be called once per run through
+	 * JSPTranslator This is intended for use by inner helper classes that
+	 * need to add block markers to their own parsers. This method only adds
+	 * markers that came from <@taglib> directives, (not <@include>), since
+	 * include file taglibs are handled on the fly when they are encountered. *
+	 * @param regions
+	 * 
+	 * @deprecated - does not properly handle prefixes
+	 */
+	protected void handleTaglib() {
+		// get/create TLDCMDocument
+		TLDCMDocumentManager mgr = TaglibController.getTLDCMDocumentManager(fStructuredDocument);
+		if (mgr != null) {
+			List trackers = mgr.getCMDocumentTrackers(getCurrentNode().getEnd());
+			Iterator it = trackers.iterator();
+			CMDocumentTracker tracker = null;
+			Iterator taglibRegions = null;
+			IStructuredDocumentRegion sdRegion = null;
+			ITextRegion r = null;
+			while (it.hasNext()) {
+				tracker = (CMDocumentTracker) it.next();
+				sdRegion = tracker.getStructuredDocumentRegion();
+				// since may be call from another thread (like a background
+				// job)
+				// this check is to be safer
+				if (sdRegion != null && !sdRegion.isDeleted()) {
+					taglibRegions = sdRegion.getRegions().iterator();
+					while (!sdRegion.isDeleted() && taglibRegions.hasNext()) {
+						r = (ITextRegion) taglibRegions.next();
+						if (r.getType().equals(DOMJSPRegionContexts.JSP_DIRECTIVE_NAME)) {
+							String text = sdRegion.getText(r);
+							if (JSP12TLDNames.TAGLIB.equals(text) || JSP12Namespace.ElementName.DIRECTIVE_TAGLIB.equals(text)) {
+								addBlockMarkers(tracker.getDocument());
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/*
+	 * This method should ideally only be called once per run through
+	 * JSPTranslator This is intended for use by inner helper classes that
+	 * need to add block markers to their own parsers. This method only adds
+	 * markers that came from <@taglib> directives, (not <@include>), since
+	 * include file taglibs are handled on the fly when they are encountered. *
+	 * @param regions
+	 */
+	private void handleTaglib(String prefix) {
+		// get/create TLDCMDocument
+		TLDCMDocumentManager mgr = TaglibController.getTLDCMDocumentManager(fStructuredDocument);
+		if (mgr != null) {
+			// get trackers for the CMDocuments enabled at this offset
+			List trackers = mgr.getCMDocumentTrackers(getCurrentNode().getEnd());
+			Iterator it = trackers.iterator();
+			CMDocumentTracker tracker = null;
+			while (it.hasNext()) {
+				tracker = (CMDocumentTracker) it.next();
+				addBlockMarkers(prefix + ":", tracker.getDocument()); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/*
+	 * adds block markers to JSPTranslator's block marker list for all
+	 * elements in doc @param doc
+	 */
+	protected void addBlockMarkers(CMDocument doc) {
+		if (doc.getElements().getLength() > 0) {
+			Iterator elements = doc.getElements().iterator();
+			CMNode node = null;
+			while (elements.hasNext()) {
+				node = (CMNode) elements.next();
+				getBlockMarkers().add(new BlockMarker(node.getNodeName(), null, DOMJSPRegionContexts.JSP_CONTENT, true));
+			}
+		}
+	}
+
+	/*
+	 * adds block markers to JSPTranslator's block marker list for all
+	 * elements in doc @param doc
+	 */
+	protected void addBlockMarkers(String prefix, CMDocument doc) {
+		if (doc.getElements().getLength() > 0) {
+			Iterator elements = doc.getElements().iterator();
+			CMNode node = null;
+			while (elements.hasNext()) {
+				node = (CMNode) elements.next();
+				if (node instanceof TLDElementDeclaration && ((TLDElementDeclaration) node).getBodycontent().equals(JSP12TLDNames.CONTENT_TAGDEPENDENT))
+					getBlockMarkers().add(new BlockMarker(prefix + node.getNodeName(), null, DOMRegionContext.BLOCK_TEXT, true));
+				else
+					getBlockMarkers().add(new BlockMarker(prefix + node.getNodeName(), null, DOMJSPRegionContexts.JSP_CONTENT, true));
+			}
+		}
+	}
+
+	/**
+	 * If r is an attribute name region, this method will safely return the
+	 * value for that attribute.
+	 * 
+	 * @param r
+	 * @param remainingRegions
+	 * @return the value for the attribute name (r), or null if isn't one
+	 */
+	protected String getAttributeValue(ITextRegion r, Iterator remainingRegions) {
+		if (r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
+			if (remainingRegions.hasNext() && (r = (ITextRegion) remainingRegions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
+				if (remainingRegions.hasNext() && (r = (ITextRegion) remainingRegions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
+					return StringUtils.stripQuotes(getCurrentNode().getText(r));
+				}
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * takes an iterator of the attributes of a page directive and the
+	 * directive itself. The iterator is used in case it can be optimized, but
+	 * the documentRegion is still required to ensure that the values are
+	 * extracted from the correct text.
+	 */
+	protected void translatePageDirectiveAttributes(Iterator regions, IStructuredDocumentRegion documentRegion) {
+		ITextRegion r = null;
+		String attrName, attrValue;
+		// iterate all attributes
+		while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() != DOMJSPRegionContexts.JSP_CLOSE) {
+			attrName = attrValue = null;
+			if (r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
+
+				attrName = documentRegion.getText(r).trim();
+				if (attrName.length() > 0) {
+					if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
+						if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
+
+							attrValue = StringUtils.strip(documentRegion.getText(r));
+						}
+						// has equals, but no value?
+					}
+					setDirectiveAttribute(attrName, attrValue);
+				}
+			}
+		}
+	}
+
+	/**
+	 * sets the appropriate page/tag directive attribute
+	 */
+	protected void setDirectiveAttribute(String attrName, String attrValue) {
+		if (attrValue == null)
+			return; // uses default (if there was one)
+		if (attrName.equals("extends")) //$NON-NLS-1$
+		{
+			fSuperclass = attrValue;
+		}
+		else if (attrName.equals("import")) //$NON-NLS-1$
+		{
+			addImports(attrValue);
+		}
+		else if (attrName.equals("session")) //$NON-NLS-1$
+		{
+			fIsInASession = Boolean.valueOf(attrValue).booleanValue();
+		}
+		else if (attrName.equals("buffer")) //$NON-NLS-1$
+		{
+			// ignore for now
+		}
+		else if (attrName.equals("autoFlush")) //$NON-NLS-1$
+		{
+			// ignore for now
+		}
+		else if (attrName.equals("isThreadSafe")) //$NON-NLS-1$
+		{
+			// fThreadSafe = "true".equalsIgnoreCase(attrValue); //$NON-NLS-1$
+		}
+		else if (attrName.equals("isErrorPage")) //$NON-NLS-1$
+		{
+			fIsErrorPage = Boolean.valueOf(attrValue).booleanValue();
+		}
+	}
+
+	protected void handleIncludeFile(String filename) {
+		if (filename != null && fProcessIncludes) {
+			IPath modelPath = getModelPath();
+			IPath basePath = modelPath;
+			if (basePath != null) {
+				/*
+				 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=227576
+				 * 
+				 * The resolution of the included fragment should use the file
+				 * containing the directive as the base reference, not always
+				 * the main JSP being invoked. Verified behavior with Apache
+				 * Tomcat 5.5.20.
+				 */
+				if (!getIncludes().isEmpty()) {
+					basePath = new Path((String) getIncludes().peek());
+				}
+				String filePathString = FacetModuleCoreSupport.resolve(basePath, filename).toString();
+				fIncludedPaths.add(filePathString);
+
+				if (!getIncludes().contains(filePathString) && !filePathString.equals(modelPath.toString())) {
+					getIncludes().push(filePathString);
+					JSPIncludeRegionHelper helper = new JSPIncludeRegionHelper(this, true);
+					// Should we consider preludes on this segment?
+					helper.parse(filePathString);
+					getIncludes().pop();
+				}
+			}
+		}
+	}
+
+	private Stack getIncludes() {
+		if (fIncludes == null)
+			fIncludes = new Stack();
+		return fIncludes;
+	}
+
+	public Collection getIncludedPaths() {
+		return fIncludedPaths;
+	}
+
+	protected void translateExpressionString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength, boolean isIndirect) {
+		appendToBuffer(EXPRESSION_PREFIX, fUserCode, false, embeddedContainer, true);
+		appendToBuffer(newText, fUserCode, true, embeddedContainer, jspPositionStart, jspPositionLength, isIndirect, true);
+		appendToBuffer(EXPRESSION_SUFFIX, fUserCode, false, embeddedContainer);
+	}
+	
+	protected void translateExpressionString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength, String quotetype) {
+		if(quotetype == null || quotetype == DOMJSPRegionContexts.XML_TAG_ATTRIBUTE_VALUE_DQUOTE ||quotetype == DOMJSPRegionContexts.XML_TAG_ATTRIBUTE_VALUE_SQUOTE ) {
+			translateExpressionString(newText, embeddedContainer, jspPositionStart, jspPositionLength, false);
+			return;
+		}
+
+		//-- This is a quoted attribute. We need to unquote as per the JSP spec: JSP 2.0 page 1-36
+		appendToBuffer(EXPRESSION_PREFIX, fUserCode, false, embeddedContainer, true);
+
+		int length = newText.length();
+		int runStart = 0;
+		int i = 0;
+		for ( ; i < length; i++) {
+			//-- collect a new run
+			char c = newText.charAt(i);
+			if (c == '\\') {
+				//-- Escaped value. Add the run, then unescape
+				int runLength = i-runStart;
+				if (runLength > 0) {
+					appendToBuffer(newText.substring(runStart, i), fUserCode, true, embeddedContainer, jspPositionStart, runLength, true, true);
+					jspPositionStart += runLength + 1;
+					jspPositionLength -= runLength + 1;
+				}
+				runStart = ++i;
+				if (i >= length) { // Escape but no data follows?!
+					//- error.
+					break;
+				}
+				c = newText.charAt(i);				// The escaped character, copied verbatim
+			}
+		}
+		//-- Copy last-run
+		int runLength = i - runStart;
+		if (runLength > 0)
+			appendToBuffer(newText.substring(runStart, i), fUserCode, true, embeddedContainer, jspPositionStart, runLength, true, false);
+		appendToBuffer(EXPRESSION_SUFFIX, fUserCode, false, embeddedContainer);
+	}
+
+	protected void translateDeclarationString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength, boolean isIndirect) {
+		appendToBuffer(newText, fUserDeclarations, true, embeddedContainer, jspPositionStart, jspPositionLength, isIndirect);
+		appendToBuffer(ENDL, fUserDeclarations, false, embeddedContainer);
+	}
+
+	/**
+	 * used by XMLJSPRegionHelper for included JSP files
+	 * 
+	 * @param newText
+	 * @param embeddedContainer
+	 * @param jspPositionStart
+	 * @param jspPositionLength
+	 */
+	protected void translateScriptletString(String newText, ITextRegionCollection embeddedContainer, int jspPositionStart, int jspPositionLength, boolean isIndirect) {
+		appendToBuffer(newText, fUserCode, true, embeddedContainer, jspPositionStart, jspPositionLength, isIndirect);
+	}
+
+	// the following 3 methods determine the cursor position
+	// <%= %>
+	protected void translateExpression(ITextRegionCollection region) {
+		String newText = getUnescapedRegionText(region, EXPRESSION);
+		appendToBuffer(EXPRESSION_PREFIX, fUserCode, false, region, true);
+		appendToBuffer(newText, fUserCode, true, region, true);
+		appendToBuffer(EXPRESSION_SUFFIX, fUserCode, false, region);
+	}
+
+	//
+	// <%! %>
+	protected void translateDeclaration(ITextRegionCollection region) {
+		String newText = getUnescapedRegionText(region, DECLARATION);
+		appendToBuffer(newText, fUserDeclarations, true, region);
+		appendToBuffer(ENDL, fUserDeclarations, false, region);
+	}
+
+	//
+	// <% %>
+	protected void translateScriptlet(ITextRegionCollection region) {
+		String newText = getUnescapedRegionText(region, SCRIPTLET);
+		appendToBuffer(newText, fUserCode, true, region);
+	}
+
+	/**
+	 * Append using a region, probably indirect mapping (eg. <%@page
+	 * include=""%>)
+	 * 
+	 * @param newText
+	 * @param buffer
+	 * @param addToMap
+	 * @param jspReferenceRegion
+	 */
+	private void appendToBuffer(String newText, StringBuffer buffer, boolean addToMap, ITextRegionCollection jspReferenceRegion) {
+		int start = 0, length = 0;
+		if (jspReferenceRegion != null) {
+			start = jspReferenceRegion.getStartOffset();
+			length = jspReferenceRegion.getLength();
+		}
+		appendToBuffer(newText, buffer, addToMap, jspReferenceRegion, start, length, false);
+	}
+	
+	/**
+	 * Append using a region, probably indirect mapping (eg. <%@page
+	 * include=""%>)
+	 * 
+	 * @param newText
+	 * @param buffer
+	 * @param addToMap
+	 * @param jspReferenceRegion
+	 * @param nonl
+	 */
+	private void appendToBuffer(String newText, StringBuffer buffer, boolean addToMap, ITextRegionCollection jspReferenceRegion, boolean nonl) {
+		int start = 0, length = 0;
+		if (jspReferenceRegion != null) {
+			start = jspReferenceRegion.getStartOffset();
+			length = jspReferenceRegion.getLength();
+		}
+		appendToBuffer(newText, buffer, addToMap, jspReferenceRegion, start, length, false, nonl);
+	}
+	
+	private void appendToBuffer(String newText, StringBuffer buffer, boolean addToMap, ITextRegionCollection jspReferenceRegion, int jspPositionStart, int jspPositionLength, boolean isIndirect) {
+		appendToBuffer(newText, buffer, addToMap, jspReferenceRegion, jspPositionStart, jspPositionLength, isIndirect, false);
+	}
+
+
+	/**
+	 * Adds newText to the buffer passed in, and adds to translation mapping
+	 * as specified by the addToMap flag. some special cases to consider (that
+	 * may be affected by changes to this method): included files scriplets in
+	 * an attribute value refactoring
+	 * 
+	 * @param newText
+	 * @param buffer
+	 * @param addToMap
+	 */
+	private void appendToBuffer(String newText, StringBuffer buffer, boolean addToMap, ITextRegionCollection jspReferenceRegion, int jspPositionStart, int jspPositionLength, boolean isIndirect, boolean nonl) {
+
+		int origNewTextLength = newText.length();
+
+		// nothing to append
+		if (jspReferenceRegion == null)
+			return;
+
+		// add a newline so translation looks cleaner
+		if (! nonl && !newText.endsWith(ENDL))
+			newText += ENDL;
+
+		//dump any non translated code before writing translated code
+		writePlaceHolderForNonTranslatedCode();
+
+		//if appending to the buffer can assume something got translated
+		fCodeTranslated = true;
+
+		if (buffer == fUserCode) {
+			buffer.append(newText);
+			if (addToMap) {
+				if (isUsebeanTag(jspReferenceRegion)) {
+					try {
+						// requires special mapping
+						appendUseBeanToBuffer(newText, jspReferenceRegion, isIndirect);
+					}
+					catch (Exception e) {
+						// still working out kinks
+						Logger.logException(e);
+					}
+				}
+				else {
+					// all other cases
+					Position javaRange = new Position(fOffsetInUserCode, origNewTextLength);
+					Position jspRange = new Position(jspPositionStart, jspPositionLength);
+
+					fCodeRanges.put(javaRange, jspRange);
+					if (isIndirect)
+						fIndirectRanges.put(javaRange, jspRange);
+				}
+			}
+			fOffsetInUserCode += newText.length();
+		}
+		else if (buffer == fUserDeclarations) {
+			buffer.append(newText);
+			if (addToMap) {
+				Position javaRange = new Position(fOffsetInUserDeclarations, newText.length());
+				Position jspRange = new Position(jspPositionStart, jspPositionLength);
+
+				fDeclarationRanges.put(javaRange, jspRange);
+				if (isIndirect)
+					fIndirectRanges.put(javaRange, jspRange);
+			}
+			fOffsetInUserDeclarations += newText.length();
+		}
+	}
+
+	/**
+	 * 
+	 * @param jspReferenceRegion
+	 * @return
+	 */
+	private boolean isUsebeanTag(ITextRegionCollection jspReferenceRegion) {
+		ITextRegionList regions = jspReferenceRegion.getRegions();
+		ITextRegion r = null;
+		boolean isUseBean = false;
+		for (int i = 0; i < regions.size(); i++) {
+			r = regions.get(i);
+			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=128490
+			// length of 11 is the length of jsp:useBean
+			// and saves the expensive getText.equals call
+			if (r.getType() == DOMRegionContext.XML_TAG_NAME) {
+				if (r.getTextLength() == 11 && jspReferenceRegion.getText(r).equals("jsp:useBean")) { //$NON-NLS-1$
+					isUseBean = true;
+				}
+				// break no matter what if you hit tagname
+				break;
+			}
+		}
+		return isUseBean;
+	}
+
+	/**
+	 * @param importName
+	 *            should be just the package plus the type eg. java.util.List
+	 *            or java.util.*
+	 * @param jspReferenceRegion
+	 *            should be the <%@ page import = "java.util.List"%> region
+	 * @param addToMap
+	 */
+	private void appendImportToBuffer(String importName, ITextRegionCollection jspReferenceRegion, boolean addToMap) {
+		String javaImportString = "import " + importName + ";" + ENDL; //$NON-NLS-1$ //$NON-NLS-2$
+		fUserImports.append(javaImportString);
+		if (addToMap) {
+			addImportToMap(importName, jspReferenceRegion);
+		}
+		fOffsetInUserImports += javaImportString.length();
+	}
+
+	/**
+	 * new text can be something like: "import java.lang.Object;\n"
+	 * 
+	 * but the reference region could have been something like: <%@page
+	 * import="java.lang.Object, java.io.*, java.util.List"%>
+	 * 
+	 * so the exact mapping has to be calculated carefully.
+	 * 
+	 * isIndirect means that the import came from an included file (if true)
+	 * 
+	 * @param importName
+	 * @param jspReferenceRegion
+	 */
+	private void addImportToMap(String importName, ITextRegionCollection jspReferenceRegion) {
+
+		// massage text
+		// String jspText = importName.substring(importName.indexOf("import ")
+		// + 7, importName.indexOf(';'));
+		// String jspText = importName.trim();
+
+		// these positions will be updated below
+		Position javaRange = new Position(fOffsetInUserImports + 7, 1);
+		Position jspRange = new Position(jspReferenceRegion.getStart(), jspReferenceRegion.getLength());
+
+		// calculate JSP range by finding "import" attribute
+		ITextRegionList regions = jspReferenceRegion.getRegions();
+		int size = regions.size();
+
+		int start = -1;
+		int length = -1;
+
+		ITextRegion r = null;
+		for (int i = 0; i < size; i++) {
+			r = regions.get(i);
+			if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)
+				if (jspReferenceRegion.getText(r).trim().equals("import")) { //$NON-NLS-1$
+					// get the attr value region
+					if (size > i + 2) {
+						r = regions.get(i + 2);
+						if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
+
+							String jspImportText = jspReferenceRegion.getText(r);
+
+							// the position in question (in the JSP) is what
+							// is bracketed below
+							// includes whitespace
+							// <%@page import="java.lang.Object,[ java.io.* ],
+							// java.util.List"%>
+
+							// in the java file
+							// import [ java.io.* ];
+
+							start = jspImportText.indexOf(importName);
+							length = importName.length();
+
+							// safety, don't add to map if bad positioning
+							if (start == -1 || length < 1)
+								break;
+
+							// update jsp range
+							jspRange.setOffset(jspReferenceRegion.getStartOffset(r) + start);
+							jspRange.setLength(length);
+
+							// update java range
+							javaRange.setLength(length);
+
+							break;
+						}
+					}
+				}
+		}
+
+		// safety for bad ranges
+		if (start != -1 && length > 1) {
+			// put ranges in java -> jsp range map
+			fImportRanges.put(javaRange, jspRange);
+		}
+	}
+
+	/**
+	 * temp fix for 282295 until better mapping is in place
+	 * 
+	 * @param newText
+	 * @param jspReferenceRegion
+	 */
+	private void appendUseBeanToBuffer(String newText, ITextRegionCollection jspReferenceRegion, boolean isIndirect) throws Exception {
+		// java string looks like this (tokenized)
+		// Type id = new Classname();\n
+		// 0 1 2 3 4
+		// or
+		// Type id = null;\n // if there is no classname
+		// 0 1 2 3
+
+		// ----------------------
+		// calculate java ranges
+		// ----------------------
+		StringTokenizer st = new StringTokenizer(newText, " ", false); //$NON-NLS-1$
+		int i = 0;
+		String[] parsedJava = new String[st.countTokens()];
+		while (st.hasMoreTokens())
+			parsedJava[i++] = st.nextToken();
+
+		String type = parsedJava[0] != null ? parsedJava[0] : ""; //$NON-NLS-1$
+		String id = parsedJava[1] != null ? parsedJava[1] : ""; //$NON-NLS-1$
+		String className = parsedJava.length > 4 ? parsedJava[4] : ""; //$NON-NLS-1$
+
+		Position javaTypeRange = new Position(fOffsetInUserCode, type.length());
+		Position javaIdRange = new Position(fOffsetInUserCode + type.length() + 1, id.length());
+		Position javaClassRange = new Position(fOffsetInUserCode + type.length() + 1 + id.length() + 7, 0);
+		/*
+		 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=212242 - Check for
+		 * the existence of '(' first.
+		 */
+		int parenPos = -1;
+		if (className.length() >= 4 && (parenPos = className.indexOf('(')) >= 0) {
+			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=86132
+			int classNameLength = className.substring(0, parenPos).length();
+			javaClassRange = new Position(fOffsetInUserCode + type.length() + 1 + id.length() + 7, classNameLength);
+		}
+
+		// ---------------------
+		// calculate jsp ranges
+		// ---------------------
+		ITextRegionList regions = jspReferenceRegion.getRegions();
+		ITextRegion r = null;
+		String attrName = "", attrValue = ""; //$NON-NLS-1$ //$NON-NLS-2$
+		int quoteOffset = 0;
+		Position jspTypeRange = null;
+		Position jspIdRange = null;
+		Position jspClassRange = null;
+
+		for (int j = 0; j < regions.size(); j++) {
+			r = regions.get(j);
+			if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
+				attrName = jspReferenceRegion.getText(r);
+				if (regions.size() > j + 2 && regions.get(j + 2).getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
+					// get attr value
+					r = regions.get(j + 2);
+					attrValue = jspReferenceRegion.getText(r);
+
+					// may have quotes
+					quoteOffset = (attrValue.startsWith("\"") || attrValue.startsWith("'")) ? 1 : 0; //$NON-NLS-1$ //$NON-NLS-2$
+
+					if (attrName.equals("type")) //$NON-NLS-1$
+						jspTypeRange = new Position(jspReferenceRegion.getStartOffset(r) + quoteOffset, StringUtils.stripQuotesLeaveInsideSpace(attrValue).length());
+					else if (attrName.equals("id")) //$NON-NLS-1$
+						jspIdRange = new Position(jspReferenceRegion.getStartOffset(r) + quoteOffset, StringUtils.stripQuotesLeaveInsideSpace(attrValue).length());
+					else if (attrName.equals("class")) //$NON-NLS-1$
+						jspClassRange = new Position(jspReferenceRegion.getStartOffset(r) + quoteOffset, StringUtils.stripQuotesLeaveInsideSpace(attrValue).length());
+				}
+			}
+		}
+
+		// put ranges in java -> jsp range map
+		if (!type.equals("") && jspTypeRange != null) { //$NON-NLS-1$
+			fCodeRanges.put(javaTypeRange, jspTypeRange);
+			// note: don't update offsets for this map when result is built
+			// they'll be updated when code ranges offsets are updated
+			fUseBeanRanges.put(javaTypeRange, jspTypeRange);
+			if (isIndirect)
+				fIndirectRanges.put(javaTypeRange, jspTypeRange);
+		}
+		if (!id.equals("") && jspIdRange != null) { //$NON-NLS-1$
+			fCodeRanges.put(javaIdRange, jspIdRange);
+			// note: don't update offsets for this map when result is built
+			// they'll be updated when code ranges offsets are updated
+			fUseBeanRanges.put(javaIdRange, jspTypeRange);
+			if (isIndirect)
+				fIndirectRanges.put(javaIdRange, jspTypeRange);
+		}
+		if (!className.equals("") && jspClassRange != null) { //$NON-NLS-1$
+			fCodeRanges.put(javaClassRange, jspClassRange);
+			// note: don't update offsets for this map when result is built
+			// they'll be updated when code ranges offsets are updated
+			fUseBeanRanges.put(javaClassRange, jspTypeRange);
+			if (isIndirect)
+				fIndirectRanges.put(javaClassRange, jspTypeRange);
+		}
+	}
+
+	/**
+	 * Set the buffer to the current JSPType: STANDARD_JSP, EMBEDDED_JSP,
+	 * DECLARATION, EXPRESSION, SCRIPTLET (for keepting track of cursor
+	 * position when the final document is built)
+	 * 
+	 * @param JSPType
+	 *            the JSP type that the cursor is in
+	 */
+	protected void setCursorOwner(int JSPType) {
+		switch (JSPType) {
+			case DECLARATION :
+				setCursorOwner(fUserDeclarations);
+				break;
+			case EXPRESSION :
+			case SCRIPTLET :
+				setCursorOwner(fUserCode);
+				break;
+			default :
+				setCursorOwner(fUserCode);
+		}
+	}
+
+	/**
+	 * this piece of code iterates through fCurrentNodes and clumps them
+	 * together in a big text string - unescaping characters if they are not
+	 * CDATA - simply appending if they are CDATA it stops iteration when it
+	 * hits a node that is an XML_TAG_NAME (which should be the region closing
+	 * tag)
+	 */
+	protected String getUnescapedRegionText(ITextRegionCollection stRegion, int JSPType) {
+		StringBuffer buffer = new StringBuffer();
+		int start = stRegion.getStartOffset();
+		int end = stRegion.getEndOffset();
+		// adjustment necessary for embedded region containers
+		if (stRegion instanceof ITextRegionContainer && stRegion.getType() == DOMRegionContext.BLOCK_TEXT) {
+			if (stRegion.getRegions() != null && stRegion.getRegions().size() > 1) {
+				ITextRegion jspContent = stRegion.getRegions().get(1); // should
+				// be
+				// jspContent
+				// region
+				start = stRegion.getStartOffset(jspContent);
+				end = stRegion.getEndOffset(jspContent);
+			}
+		}
+		int CDATAOffset = 0; // number of characters lost in conversion
+		int bufferSize = 0;
+		if (stRegion.getType() == DOMJSPRegionContexts.JSP_CONTENT || stRegion.getType() == DOMRegionContext.BLOCK_TEXT // need
+					// this
+					// for
+					// embedded
+					// JSP
+					// regions
+					|| stRegion.getType() == DOMRegionContext.XML_TAG_NAME) // need
+		// this
+		// in
+		// case
+		// there's
+		// no
+		// region...
+		{
+			fInCodeRegion = (start <= fSourcePosition && fSourcePosition <= end);
+			if (fInCodeRegion) {
+				setCursorOwner(JSPType);
+				setRelativeOffset((fSourcePosition - start) + getCursorOwner().length());
+				if (JSPType == EXPRESSION) {
+					// if an expression, add then length of the enclosing
+					// paren..
+					setCursorInExpression(true);
+					setRelativeOffset(getRelativeOffset() + EXPRESSION_PREFIX.length());
+				}
+			}
+			ITextRegion jspContent = null;
+			if (stRegion.getRegions() != null && stRegion.getRegions().size() > 1)
+				jspContent = stRegion.getRegions().get(1);
+			return (jspContent != null) ? stRegion.getFullText(jspContent) : stRegion.getFullText(); // don't
+			// unescape
+			// if
+			// it's
+			// not
+			// an
+			// XMLJSP
+			// tag
+		}
+		else if (stRegion.getType() == DOMJSPRegionContexts.JSP_CLOSE) {
+			// need to determine cursor owner so that the fCurosorPosition
+			// will be
+			// correct even if there is no region after the cursor in the JSP
+			// file
+			setCursorOwner(JSPType);
+		}
+		// iterate XMLCONTENT and CDATA regions
+		// loop fCurrentNode until you hit </jsp:scriptlet> (or other closing
+		// tag name)
+		while (getCurrentNode() != null && getCurrentNode().getType() != DOMRegionContext.XML_TAG_NAME && getCurrentNode().getType() != DOMJSPRegionContexts.JSP_CLOSE) // need to stop on the ending tag name...
+		{
+			start = getCurrentNode().getStartOffset();
+			end = getCurrentNode().getEndOffset();
+			bufferSize = buffer.length();
+			CDATAOffset = unescapeRegion(getCurrentNode(), buffer);
+			fInCodeRegion = (start <= fSourcePosition && fSourcePosition <= end);
+			if (fInCodeRegion) {
+				setCursorOwner(JSPType);
+				// this offset is sort of complicated...
+				// it's composed of:
+				// 1. the length of the start of the current region up till
+				// where the cursor is
+				// 2. minus the number of characters lost in CDATA translation
+				// 3. plus the length of the escaped buffer before the current
+				// region, but
+				// is still within the jsp tag
+				setRelativeOffset((fSourcePosition - getCurrentNode().getStartOffset()) + getCursorOwner().length() - CDATAOffset + bufferSize);
+				if (JSPType == EXPRESSION) {
+					setCursorInExpression(true);
+					// if an expression, add then length of the enclosing
+					// paren..
+					setRelativeOffset(getRelativeOffset() + EXPRESSION_PREFIX.length());
+				}
+			}
+			if (getCurrentNode() != null)
+				advanceNextNode();
+		}
+		return buffer.toString();
+	}
+
+	/**
+	 * @param r
+	 *            the region to be unescaped (XMLContent, XML ENTITY
+	 *            REFERENCE, or CDATA)
+	 * @param sb
+	 *            the stringbuffer to append the text to
+	 * @return the number of characters removed in unescaping this text
+	 */
+	protected int unescapeRegion(ITextRegion r, StringBuffer sb) {
+		String s = ""; //$NON-NLS-1$
+		int lengthBefore = 0, lengthAfter = 0, cdata_tags_length = 0;
+		if (r != null && (r.getType() == DOMRegionContext.XML_CONTENT || r.getType() == DOMRegionContext.XML_ENTITY_REFERENCE)) {
+			lengthBefore = (getCurrentNode() != r) ? getCurrentNode().getFullText(r).length() : getCurrentNode().getFullText().length();
+			s = EscapedTextUtil.getUnescapedText(getCurrentNode(), r);
+			lengthAfter = s.length();
+			sb.append(s);
+		}
+		else if (r != null && r.getType() == DOMRegionContext.XML_CDATA_TEXT) {
+			if (r instanceof ITextRegionContainer) // only interested in
+			// contents
+			{
+				// navigate to next region container (which should be a JSP
+				// region)
+				Iterator it = ((ITextRegionContainer) r).getRegions().iterator();
+				ITextRegion temp = null;
+				while (it.hasNext()) {
+					temp = (ITextRegion) it.next();
+					if (temp instanceof ITextRegionContainer || temp.getType() == DOMRegionContext.XML_CDATA_TEXT) {
+						sb.append(getCurrentNode().getFullText(temp));
+					}
+					else if (temp.getType() == DOMRegionContext.XML_CDATA_OPEN || temp.getType() == DOMRegionContext.XML_CDATA_CLOSE) {
+						cdata_tags_length += temp.getLength();
+					}
+				}
+			}
+		}
+		return (lengthBefore - lengthAfter + cdata_tags_length);
+	}
+
+	//
+	// <jsp:useBean>
+	protected void translateUseBean(ITextRegionCollection container) {
+		ITextRegion r = null;
+		String attrName = null;
+		String attrValue = null;
+		String id = null;
+		ITextRegion idRegion = null;
+		String type = null;
+		ITextRegion typeRegion = null;
+		String className = null;
+		ITextRegion classnameRegion = null;
+		String beanName = null;
+		ITextRegion beanNameRegion = null;
+
+		if (DOMRegionContext.XML_END_TAG_OPEN.equals(container.getFirstRegion().getType())) {
+			if (!fUseBeansStack.isEmpty()) {
+				fUseBeansStack.pop();
+				appendToBuffer("}", fUserCode, false, fCurrentNode); //$NON-NLS-1$ 
+			}
+			else {
+				// no useBean start tag being remembered
+				ITextRegionCollection extraEndRegion = container;
+				IJSPProblem missingStartTag = createJSPProblem(IJSPProblem.UseBeanStartTagMissing, IJSPProblem.F_PROBLEM_ID_LITERAL, NLS.bind(JSPCoreMessages.JSPTranslator_4,JSP11Namespace.ElementName.USEBEAN), extraEndRegion.getStartOffset(), extraEndRegion.getEndOffset());
+				fTranslationProblems.add(missingStartTag);
+			}
+			return;
+		}
+
+		Iterator regions = container.getRegions().iterator();
+		while (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && (r.getType() != DOMRegionContext.XML_TAG_CLOSE || r.getType() != DOMRegionContext.XML_EMPTY_TAG_CLOSE)) {
+			attrName = attrValue = null;
+			if (r.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
+				attrName = container.getText(r).trim();
+				if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
+					if (regions.hasNext() && (r = (ITextRegion) regions.next()) != null && r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
+						attrValue = StringUtils.stripQuotes(container.getText(r));
+					}
+					// has equals, but no value?
+				}
+				// an attribute with no equals?
+			}
+			// (pa) might need different logic here if we wanna support more
+			if (attrName != null && attrValue != null) {
+				if (attrName.equals("id")) {//$NON-NLS-1$
+					id = attrValue;
+					idRegion = r;
+				}
+				else if (attrName.equals("class")) {//$NON-NLS-1$
+					className = attrValue;
+					classnameRegion = r;
+				}
+				else if (attrName.equals("type")) {//$NON-NLS-1$
+					type = attrValue;
+					typeRegion = r;
+				}
+				else if (attrName.equals("beanName")) { //$NON-NLS-1$
+					beanName = attrValue;
+					beanNameRegion = r;
+				}
+			}
+		}
+
+		if (id != null) {
+			// The id is not a valid Java identifier
+			if (!isValidJavaIdentifier(id) && idRegion != null) {
+				Object problem = createJSPProblem(IJSPProblem.UseBeanInvalidID, IProblem.ParsingErrorInvalidToken, MessageFormat.format(JSPCoreMessages.JSPTranslator_0, new String[]{id}), container.getStartOffset(idRegion), container.getTextEndOffset(idRegion) - 1);
+				fTranslationProblems.add(problem);
+			}
+			// No Type information is provided
+			if (((type == null && className == null) || (type == null && beanName != null)) && idRegion != null) {
+				Object problem = createJSPProblem(IJSPProblem.UseBeanMissingTypeInfo, IProblem.UndefinedType, NLS.bind(JSPCoreMessages.JSPTranslator_3, new String[]{id}), container.getStartOffset(idRegion), container.getTextEndOffset(idRegion) - 1);
+				fTranslationProblems.add(problem);
+			}
+			// Cannot specify both a class and a beanName
+			if (className != null && beanName != null && beanNameRegion != null) {
+				ITextRegion nameRegion = container.getRegions().get(1);
+				Object problem = createJSPProblem(IJSPProblem.UseBeanAmbiguousType, IProblem.AmbiguousType, JSPCoreMessages.JSPTranslator_2, container.getStartOffset(nameRegion), container.getTextEndOffset(nameRegion) - 1);
+				fTranslationProblems.add(problem);
+			}
+			/*
+			 * Only have a class or a beanName at this point, and potentially
+			 * a type has id w/ type and/or classname/beanName
+			 */
+			// Type id = new Classname/Beanname();
+			// or
+			// Type id = null; // if there is no classname or beanname
+			if ((type != null || className != null)) {
+				if (className != null)
+					className = decodeType(className);
+
+				if (type == null) {
+					type = className;
+					typeRegion = classnameRegion;
+				}
+				else
+					type = decodeType(type);
+
+				/* Now check the types (multiple of generics may be involved) */
+				List errorTypeNames = new ArrayList(2);
+				if (!isTypeFound(type, errorTypeNames)) {
+					for (int i = 0; i < errorTypeNames.size(); i++) {
+						Object problem = createJSPProblem(IJSPProblem.F_PROBLEM_ID_LITERAL, IProblem.UndefinedType, MessageFormat.format(JSPCoreMessages.JSPTranslator_1, new String[]{errorTypeNames.get(i).toString()}), container.getStartOffset(typeRegion), container.getTextEndOffset(typeRegion) - 1);
+						fTranslationProblems.add(problem);
+					}
+				}
+				else {
+					String prefix = type + " " + id + " = "; //$NON-NLS-1$ //$NON-NLS-2$
+					String suffix = "null;" + ENDL; //$NON-NLS-1$
+					if (className != null)
+						suffix = "new " + className + "();" + ENDL; //$NON-NLS-1$ //$NON-NLS-2$
+					else if (beanName != null)
+						suffix = "(" + type + ") java.beans.Beans.instantiate(getClass().getClassLoader(), \"" + beanName + "\");" + ENDL; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+					appendToBuffer(prefix + suffix, fUserCode, true, fCurrentNode);
+				}
+			}
+		}
+		/*
+		 * Add a brace and remember the start tag regardless of whether a
+		 * variable was correctly created
+		 */
+		if (!DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(container.getLastRegion().getType())) {
+			fUseBeansStack.push(container);
+			appendToBuffer("{", fUserCode, false, fCurrentNode); //$NON-NLS-1$ 
+		}
+	}
+
+	/**
+	 * Decodes type strings for XML-style JSPs
+	 * 
+	 * @param type the string to decode
+	 * @return the decoded string
+	 */
+	private String decodeType(String type) {
+		final int length = type.length();
+		final StringBuffer buffer = new StringBuffer(length);
+		for (int i = 0; i < length; i++) {
+			final char c = type.charAt(i);
+			if (c == '&') {
+				if (length > i + 3) {
+					final String code = type.substring(i + 1, i + 4);
+					final boolean isGt = "gt;".equals(code); //$NON-NLS-1$
+					if (isGt || "lt;".equals(code)) { //$NON-NLS-1$
+						i+=3;
+						buffer.append(isGt ? '>' : '<');
+						continue;
+					}
+				}
+			}
+			buffer.append(c);
+		}
+		return buffer.toString();
+	}
+
+	/**
+	 * @param type
+	 * @return
+	 */
+	private boolean isTypeFound(String rawTypeValue, List errorTypeNames) {
+		IFile file = getFile();
+		if(file == null)
+			return true;
+		
+		IProject project = file.getProject();
+		IJavaProject p = JavaCore.create(project);
+		if (p.exists()) {
+			String types[] = new String[3];
+			if (rawTypeValue.indexOf('<') > 0) {
+				// JSR 14 : Generics are being used, parse them out
+				try {
+					StringTokenizer toker = new StringTokenizer(rawTypeValue);
+					// generic
+					types[0] = toker.nextToken("<"); //$NON-NLS-1$
+					// type 1 or key
+					types[1] = toker.nextToken(",>"); //$NON-NLS-1$
+					// type 2 or value
+					types[2] = toker.nextToken(",>"); //$NON-NLS-1$
+				}
+				catch (NoSuchElementException e) {
+					// StringTokenizer failure with unsupported syntax
+				}
+			}
+			else {
+				types[0] = rawTypeValue;
+			}
+
+			for (int i = 0; i < types.length; i++) {
+				if (types[i] != null) {
+					// remove any array suffixes 
+					if (types[i].indexOf('[') > 0) {
+						types[i] = types[i].substring(0, types[i].indexOf('['));
+					}
+					// remove any "extends" prefixes (JSR 14)
+					if (types[i].indexOf("extends") > 0) { //$NON-NLS-1$
+						types[i] = StringUtils.strip(types[i].substring(types[i].indexOf("extends"))); //$NON-NLS-1$
+					}
+
+					addNameToListIfTypeNotFound(p, types[i], errorTypeNames);
+				}
+			}
+		}
+		return errorTypeNames.isEmpty();
+	}
+	
+	private void addNameToListIfTypeNotFound(IJavaProject p, String typeName, List collectedNamesNotFound) {
+		try {
+			if (typeName != null) {
+				IType type = p.findType(typeName);
+				if (type == null || !type.exists()) {
+					collectedNamesNotFound.add(typeName);
+				}
+				else {
+					IResource typeResource = type.getResource();
+					if(typeResource != null) {
+						
+					}
+				}
+			}
+		}
+		catch (JavaModelException e) {
+			// Not a Java Project
+		}
+	}
+
+	private boolean isValidJavaIdentifier(String id) {
+		char[] idChars = id.toCharArray();
+		if (idChars.length < 1)
+			return false;
+		boolean isValid = Character.isJavaIdentifierStart(idChars[0]);
+		for (int i = 1; i < idChars.length; i++) {
+			isValid = isValid && Character.isJavaIdentifierPart(idChars[i]);
+		}
+		return isValid;
+	}
+
+	final public int getCursorPosition() {
+		return fCursorPosition;
+	}
+
+	protected boolean isCursorInExpression() {
+		return fCursorInExpression;
+	}
+
+	protected void setCursorInExpression(boolean in) {
+		fCursorInExpression = in;
+	}
+
+	final public void setSourceCursor(int i) {
+		fSourcePosition = i;
+	}
+
+	final public int getSourcePosition() {
+		return fSourcePosition;
+	}
+
+	final public TLDCMDocumentManager getTLDCMDocumentManager() {
+		return TaglibController.getTLDCMDocumentManager(fStructuredDocument);
+	}
+
+	final public void setRelativeOffset(int relativeOffset) {
+		this.fRelativeOffset = relativeOffset;
+	}
+
+	final public int getRelativeOffset() {
+		return fRelativeOffset;
+	}
+
+	private void setCursorOwner(StringBuffer cursorOwner) {
+		this.fCursorOwner = cursorOwner;
+	}
+
+	final public StringBuffer getCursorOwner() {
+		return fCursorOwner;
+	}
+
+	private IStructuredDocumentRegion setCurrentNode(IStructuredDocumentRegion currentNode) {
+		return this.fCurrentNode = currentNode;
+	}
+
+	final public IStructuredDocumentRegion getCurrentNode() {
+		return fCurrentNode;
+	}
+
+	public IStructuredDocument getStructuredDocument() {
+		return fStructuredDocument;
+	}
+
+	private IPath getModelPath() {
+		IPath path = null;
+		ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(getStructuredDocument());
+		if (buffer != null) {
+			path = buffer.getLocation();
+		}
+		return path;
+	}
+
+	private void translateCodas() {
+		fProcessIncludes = false;
+		IPath modelpath = getModelPath();
+		if (modelpath != null) {
+			PropertyGroup[] propertyGroups = DeploymentDescriptorPropertyCache.getInstance().getPropertyGroups(modelpath);
+			for (int j = 0; j < propertyGroups.length; j++) {
+				IPath[] codas = propertyGroups[j].getIncludeCoda();
+				for (int i = 0; i < codas.length; i++) {
+					if (!getIncludes().contains(codas[i].toString()) && !codas[i].equals(modelpath)) {
+						getIncludes().push(codas[i]);
+						JSPIncludeRegionHelper helper = new JSPIncludeRegionHelper(this, true);
+						helper.parse(codas[i].toString());
+						getIncludes().pop();
+					}
+				}
+			}
+		}
+		fProcessIncludes = true;
+	}
+
+	private void translatePreludes() {
+		fProcessIncludes = false;
+		IPath modelpath = getModelPath();
+		if (modelpath != null) {
+			PropertyGroup[] propertyGroups = DeploymentDescriptorPropertyCache.getInstance().getPropertyGroups(modelpath);
+			for (int j = 0; j < propertyGroups.length; j++) {
+				IPath[] preludes = propertyGroups[j].getIncludePrelude();
+				for (int i = 0; i < preludes.length; i++) {
+					if (!getIncludes().contains(preludes[i].toString()) && !preludes[i].equals(modelpath)) {
+						getIncludes().push(preludes[i]);
+						JSPIncludeRegionHelper helper = new JSPIncludeRegionHelper(this, true);
+						helper.parse(preludes[i].toString());
+						getIncludes().pop();
+					}
+				}
+			}
+		}
+		fProcessIncludes = true;
+	}
+
+	/**
+	 * <p>Writes an empty expression to {@link #fUserCode} if previously
+	 * found non translated code</p>
+	 * <p>This should be done before appending any newly translated code.</p>
+	 */
+	private void writePlaceHolderForNonTranslatedCode() {
+		if(fFoundNonTranslatedCode) {
+			String text = ("{" + EXPRESSION_PREFIX + "\"\"" + EXPRESSION_SUFFIX + "}" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+					" //markup"+ ENDL); //$NON-NLS-1$
+			fUserCode.append(text);
+			fOffsetInUserCode += text.length();
+			fFoundNonTranslatedCode = false;
+		}
+	}
+
+	/**
+	 * <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
+	 *  {@link #readExternal(ObjectInput)} and {@link #serialVersionUID}</p>
+	 *
+	 * @see #readExternal(ObjectInput)
+	 * @see #serialVersionUID
+	 * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+	 */
+	public void writeExternal(ObjectOutput out) throws IOException {
+		writeString(out, this.fClassHeader);
+		writeString(out, this.fClassname);
+		writeString(out, this.fSuperclass);
+		writeString(out, this.fImplicitImports);
+		writeString(out, this.fServiceHeader);
+		writeBuffer(out, this.fUserImports);
+		out.writeInt(this.fSourcePosition);
+		out.writeInt(this.fRelativeOffset);
+		out.writeInt(this.fCursorPosition);
+		out.writeBoolean(this.fIsErrorPage);
+		out.writeBoolean(this.fCursorInExpression);
+		out.writeBoolean(this.fIsInASession);
+		writeBuffer(out, this.fUserCode);
+		writeBuffer(out, this.fUserELExpressions);
+		writeBuffer(out, this.fUserDeclarations);
+		writeBuffer(out, this.fCursorOwner);
+		out.writeBoolean(this.fInCodeRegion);
+		out.writeBoolean(this.fProcessIncludes);
+		out.writeInt(this.fOffsetInUserImports);
+		out.writeInt(this.fOffsetInUserDeclarations);
+		out.writeInt(this.fOffsetInUserCode);
+		
+		//write included paths
+		out.writeInt(this.fIncludedPaths.size());
+		Iterator iter = this.fIncludedPaths.iterator();
+		while(iter.hasNext()) {
+			writeString(out, (String)iter.next());
+		}
+		
+		writeRanges(out, this.fImportRanges);
+		writeRanges(out, this.fCodeRanges);
+		writeRanges(out, this.fDeclarationRanges);
+		writeRanges(out, this.fUseBeanRanges);
+		writeRanges(out, this.fUserELRanges);
+		writeRanges(out, this.fIndirectRanges);
+		writeString(out, this.fELTranslatorID);
+	}
+	
+	/**
+	 * <p><b>NOTE 1: </b>After reading in an externalized {@link JSPTranslator} the caller must
+	 * manually call {@link #postReadExternalSetup(IStructuredModel)} to finish setting up
+	 * the {@link JSPTranslator} for use.</p>
+	 * 
+	 * <p><b>NOTE 2: </b>If the implementation of this method is changed be sure to update
+	 * {@link #writeExternal(ObjectOutput)} and {@link #serialVersionUID}</p>
+	 * 
+	 * @see #writeExternal(ObjectOutput)
+	 * @see #serialVersionUID
+	 * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+	 */
+	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+		this.fClassHeader = readString(in);
+		this.fClassname = readString(in);
+		this.fSuperclass = readString(in);
+		this.fImplicitImports = readString(in);
+		this.fServiceHeader = readString(in);
+		this.fUserImports = new StringBuffer(readString(in));
+		this.fSourcePosition = in.readInt();
+		this.fRelativeOffset = in.readInt();
+		this.fCursorPosition = in.readInt();
+		this.fIsErrorPage = in.readBoolean();
+		this.fCursorInExpression = in.readBoolean();
+		this.fIsInASession = in.readBoolean();
+		this.fUserCode = new StringBuffer(readString(in));
+		this.fUserELExpressions = new StringBuffer(readString(in));
+		this.fUserDeclarations = new StringBuffer(readString(in));
+		this.fCursorOwner = new StringBuffer(readString(in));
+		this.fInCodeRegion = in.readBoolean();
+		this.fProcessIncludes = in.readBoolean();
+		this.fOffsetInUserImports = in.readInt();
+		this.fOffsetInUserDeclarations = in.readInt();
+		this.fOffsetInUserCode = in.readInt();
+		
+		//read included paths
+		int size = in.readInt();
+		this.fIncludedPaths = new HashSet(size);
+		for(int i = 0; i < size; ++i) {
+			this.fIncludedPaths.add(readString(in));
+		}
+		
+		this.fImportRanges = readRanges(in);
+		this.fCodeRanges = readRanges(in);
+		this.fDeclarationRanges = readRanges(in);
+		this.fUseBeanRanges = readRanges(in);
+		this.fUserELRanges = readRanges(in);
+		this.fIndirectRanges = readRanges(in);
+		this.fELTranslatorID = readString(in);
+		
+		//build result
+		this.buildResult(false);
+	}
+	
+	/**
+	 * <p>This does mandatory setup needed after a JSPTranslator is restored from
+	 * a persisted file</p>
+	 * 
+	 * @param jspModel {@link IStructuredModel} representing the JSP file that this
+	 * is a translator for
+	 */
+	protected void postReadExternalSetup(IStructuredModel jspModel) {
+		this.fStructuredDocument = jspModel.getStructuredDocument();
+		this.fJspTextBuffer = new StringBuffer(this.fStructuredDocument.get());
+		if(jspModel instanceof IDOMModel) {
+			this.fStructuredModel = (IDOMModel)jspModel;
+		}
+	}
+	
+	/**
+	 * <p>Writes a string to an {@link ObjectOutput} stream</p>
+	 * 
+	 * <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
+	 * {@link #readString(ObjectInput)} and {@link #serialVersionUID}</p>
+	 * 
+	 * @param out {@link ObjectOutput} stream to write <code>s</code> too
+	 * @param s {@link String} to write to <code>out</code>
+	 * 
+	 * @throws IOException IO can throw exceptions
+	 * 
+	 * @see #readString(ObjectInput)
+	 */
+	private static void writeString(ObjectOutput out, String s) throws IOException {
+		if(s != null) {
+			out.writeInt(s.length());
+			out.writeChars(s);
+		} else {
+			out.writeInt(0);
+		}
+	}
+	
+	/**
+	 * <p>Reads a {@link String} written by {@link #writeString(ObjectOutput, String)} from
+	 * a {@link ObjectInput} stream.</p>
+	 * 
+	 * <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
+	 * {@link #writeString(ObjectOutput, String)} and {@link #serialVersionUID}</p>
+	 * 
+	 * @param in {@link ObjectInput} stream to read the {@link String} from
+	 * @return {@link String} read from <code>in</code>
+	 * 
+	 * @throws IOException IO Can throw exceptions
+	 * 
+	 * @see #writeString(ObjectOutput, String)
+	 */
+	private static String readString(ObjectInput in) throws IOException {
+		int length = in.readInt();
+		char charArray[] = new char[length];
+		for(int i=0; i < length;i++){
+			charArray[i] = in.readChar();
+		}
+		return new String(charArray);
+	}
+	
+	/**
+	 * <p>Writes a {@link StringBuffer} to an {@link ObjectOutput} stream</p>
+	 * 
+	 * @param out {@link ObjectOutput} stream to write <code>s</code> too
+	 * @param s {@link String} to write to <code>out</code>
+	 * 
+	 * @throws IOException IO can throw exceptions
+	 * 
+	 * @see #readString(ObjectInput)
+	 */
+	private static void writeBuffer(ObjectOutput out, StringBuffer buff) throws IOException {
+		if(buff != null && buff.length() > 0) {
+			writeString(out, buff.toString());
+		} else {
+			writeString(out, null);
+		}
+	}
+	
+	/**
+	 * <p>Writes a {@link HashMap} of {@link Position}s to an {@link ObjectOutput} stream</p>
+	 * 
+	 * <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
+	 * {@link #readRanges(ObjectInput)} and {@link #serialVersionUID}</p>
+	 * 
+	 * @param out {@link ObjectOutput} stream to write to
+	 * @param ranges {@link HashMap} of {@link Position}s to write to <code>out</code>
+	 * 
+	 * @throws IOException IO can throw exceptions
+	 * 
+	 * @see #readRanges(ObjectInput)
+	 */
+	private static void writeRanges(ObjectOutput out, HashMap ranges) throws IOException {
+		//this is a strange hack because Position is not designed to be used as keys in a Map, see Position doc
+		HashMap temp = new HashMap();
+		temp.putAll(ranges);
+		
+		Iterator iter = temp.keySet().iterator();
+		out.writeInt(ranges.size());
+		while(iter.hasNext()) {
+			Position javaPos = (Position)iter.next();
+			Position jspPos = (Position)temp.get(javaPos);
+			out.writeInt(javaPos.offset);
+			out.writeInt(javaPos.length);
+			out.writeBoolean(javaPos.isDeleted);
+			
+			if(jspPos != null) {
+				out.writeInt(jspPos.offset);
+				out.writeInt(jspPos.length);
+				out.writeBoolean(jspPos.isDeleted);
+			} else {
+				out.writeInt(-1);
+				out.writeInt(-1);
+			}
+		}
+	}
+	
+	/**
+	 * <p>Reads a {@link HashMap} of {@link Position}s from an {@link ObjectInput} stream that was written by
+	 * {@link #writeRanges(ObjectOutput, HashMap)}</p>
+	 * 
+	 * <p><b>NOTE: </b>If the implementation of this method is changed be sure to update
+	 * {@link #writeRanges(ObjectOutput, HashMap)} and {@link #serialVersionUID}</p>
+	 * 
+	 * @param in {@link ObjectInput} stream to read the {@link HashMap} of {@link Position}s from
+	 * @return {@link HashMap} of {@link Position}s read from <code>in</code>
+	 * 
+	 * @throws IOException IO can throw exceptions
+	 * 
+	 * @see #writeRanges(ObjectOutput, HashMap)
+	 */
+	private static HashMap readRanges(ObjectInput in) throws IOException {
+		int size = in.readInt();
+		HashMap ranges = new HashMap(size);
+		for(int i = 0; i < size; ++i) {
+			Position javaPos = new Position(in.readInt(), in.readInt());
+			if(in.readBoolean()) {
+				javaPos.delete();
+			}
+			
+			//if the jspPos was null for some reason then -1 was written for length and offset
+			Position jspPos = null;
+			int jspPosOffset = in.readInt();
+			int jspPosLength = in.readInt();
+			if(jspPosOffset != -1 && jspPosLength != -1) {
+				jspPos = new Position(jspPosOffset, jspPosLength);
+			}
+			
+			//only read a boolean if the jspPos was not null
+			if(jspPos != null && in.readBoolean()) {
+				jspPos.delete();
+			}
+			
+			ranges.put(javaPos, jspPos);
+		}
+		
+		return ranges;
+	}
+	
+	/**
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	public boolean equals(Object obj) {
+		boolean equal = false;
+		if(obj instanceof JSPTranslator) {
+			JSPTranslator other = (JSPTranslator)obj;
+			equal = this.fClassHeader.equals(other.fClassHeader) &&
+				this.fClassname.equals(other.fClassname) &&
+				this.fSuperclass.equals(other.fSuperclass) &&
+				this.fImplicitImports.equals(other.fImplicitImports) &&
+				this.fServiceHeader.equals(other.fServiceHeader) &&
+				buffersEqual(this.fUserImports, other.fUserImports) &&
+				this.fSourcePosition == other.fSourcePosition &&
+				this.fRelativeOffset == other.fRelativeOffset &&
+				this.fCursorPosition == other.fCursorPosition &&
+				this.fIsErrorPage == other.fIsErrorPage &&
+				this.fCursorInExpression == other.fCursorInExpression &&
+				this.fIsInASession == other.fIsInASession &&
+				buffersEqual(this.fUserCode, other.fUserCode) &&
+				buffersEqual(this.fUserELExpressions, other.fUserELExpressions) &&
+				buffersEqual(this.fUserDeclarations, other.fUserDeclarations) &&
+				buffersEqual(this.fCursorOwner, other.fCursorOwner) &&
+				this.fInCodeRegion == other.fInCodeRegion &&
+				this.fProcessIncludes == other.fProcessIncludes &&
+				this.fOffsetInUserImports == other.fOffsetInUserImports &&
+				this.fOffsetInUserDeclarations == other.fOffsetInUserDeclarations &&
+				this.fOffsetInUserCode == other.fOffsetInUserCode &&
+				rangesEqual(this.fImportRanges, other.fImportRanges) &&
+				rangesEqual(this.fCodeRanges, other.fCodeRanges) &&
+				rangesEqual(this.fDeclarationRanges, other.fDeclarationRanges) &&
+				rangesEqual(this.fUseBeanRanges, other.fUseBeanRanges) &&
+				rangesEqual(this.fUserELRanges, other.fUserELRanges) &&
+				rangesEqual(this.fIndirectRanges, other.fIndirectRanges) &&
+				(
+					(this.fELTranslatorID != null && other.fELTranslatorID != null) ||
+					(this.fELTranslatorID == null && other.fELTranslatorID != null && other.fELTranslatorID.length() == 0) ||
+					(this.fELTranslatorID != null && this.fELTranslatorID.length() == 0 && other.fELTranslatorID == null) ||
+					(this.fELTranslatorID.equals(other.fELTranslator))
+				);
+		}
+		return equal;
+	}
+	
+	/**
+	 * <p><code>null</code> is considered equivlent to an empty buffer</p>
+	 * 
+	 * @param buff1 can be <code>null</code>
+	 * @param buff2 can be <code>null</code>
+	 * @return <code>true</code> if the two given buffers are equal, <codee>false</code> otherwise
+	 */
+	private static boolean buffersEqual(StringBuffer buff1, StringBuffer buff2) {
+		return (buff1 == null && buff2 == null) ||
+			(buff1 != null && buff2!= null && buff1.toString().equals(buff2.toString())) ||
+			(buff1 == null && buff2 != null && buff2.length() == 0) ||
+			(buff1 != null && buff1.length() == 0 && buff2 == null);
+	}
+	
+	/**
+	 * @param ranges1
+	 * @param ranges2
+	 * @return <code>true</code> if the two maps of ranges contains the same key/value pares,
+	 * <code>false</code> otherwise
+	 */
+	private static boolean rangesEqual(HashMap ranges1, HashMap ranges2) {
+		//this is a strange hack because Position is not designed to be used as keys in a Map, see Position doc
+		HashMap temp = new HashMap();
+		temp.putAll(ranges1);
+		ranges1 = temp;
+		
+		boolean equal = false;
+		if(ranges1 != null && ranges2 != null) {
+			equal = true;
+			Iterator ranges1Keys = ranges1.keySet().iterator();
+			while(ranges1Keys.hasNext() && equal) {
+				Position key = (Position)ranges1Keys.next();
+				Position ranges1Value = (Position)ranges1.get(key);
+				Position ranges2Value = (Position)ranges2.get(key);
+				equal = ranges1Value.equals(ranges2Value);
+			}
+		}
+		
+		return equal;
+	}
+}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/CommonXML.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/CommonXML.java
new file mode 100644
index 0000000..3db336c
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/util/CommonXML.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jst.jsp.core.internal.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.eclipse.jst.jsp.core.internal.Logger;
+import org.w3c.dom.Document;
+
+public class CommonXML {
+
+	public synchronized static DocumentBuilder getDocumentBuilder() {
+		DocumentBuilder result = null;
+		try {
+			result = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+		}
+		catch (ParserConfigurationException e) {
+			Logger.logException(e);
+		}
+		return result;
+	}
+
+	public synchronized static DocumentBuilder getDocumentBuilder(boolean validating) {
+		DocumentBuilder result = null;
+		try {
+			DocumentBuilderFactory instance = DocumentBuilderFactory.newInstance();
+			instance.setValidating(validating);
+			instance.setExpandEntityReferences(false);
+			instance.setCoalescing(true);
+			result = instance.newDocumentBuilder();
+		}
+		catch (ParserConfigurationException e) {
+			Logger.logException(e);
+		}
+		return result;
+	}
+
+	public static void serialize(Document document, OutputStream ostream) throws IOException {
+		Source domSource = new DOMSource(document);
+		try {
+			Transformer serializer = TransformerFactory.newInstance().newTransformer();
+			try {
+				serializer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
+				serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$
+				serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-16"); //$NON-NLS-1$
+			}
+			catch (IllegalArgumentException e) {
+				// unsupported properties
+			}
+			serializer.transform(domSource, new StreamResult(ostream));
+		}
+		catch (TransformerConfigurationException e) {
+			throw new IOException(e.getMessage());
+		}
+		catch (TransformerFactoryConfigurationError e) {
+			throw new IOException(e.getMessage());
+		}
+		catch (TransformerException e) {
+			throw new IOException(e.getMessage());
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jst.jsp.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.jst.jsp.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..02d7fea
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,67 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jst.jsp.ui; singleton:=true
+Bundle-Version: 1.1.601.qualifier
+Bundle-Activator: org.eclipse.jst.jsp.ui.internal.JSPUIPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.jst.jsp.css.ui.internal.properties;x-internal:=true,
+ org.eclipse.jst.jsp.css.ui.internal.registry;x-internal:=true,
+ org.eclipse.jst.jsp.css.ui.internal.views.properties;x-internal:=true,
+ org.eclipse.jst.jsp.ui,
+ org.eclipse.jst.jsp.ui.internal;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.autoedit;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.breakpointproviders;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.contentassist;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.contentproperties.ui;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.derived;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.editor;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.format;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.handlers;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.hyperlink;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.java.refactoring;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.java.search;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.java.search.ui;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.preferences;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.preferences.ui;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.registry;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.style;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.style.java;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.style.jspel;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.taginfo;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.templates;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.text;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.validation;x-internal:=true,
+ org.eclipse.jst.jsp.ui.internal.wizard;x-internal:=true,
+ org.eclipse.jst.jsp.ui.views.contentoutline
+Import-Package: com.ibm.icu.util; version="3.8",
+ com.ibm.icu.text; version="3.8"
+Require-Bundle: org.eclipse.ui.ide;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.workbench.texteditor;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.forms;bundle-version="[3.4.0,4.0.0]",
+ org.eclipse.wst.sse.ui;bundle-version="[1.2.0,1.4.0)",
+ org.eclipse.wst.html.ui;bundle-version="[1.0.500,1.1.0)",
+ org.eclipse.wst.css.ui;bundle-version="[1.0.500,1.1.0)",
+ org.eclipse.wst.xml.ui;bundle-version="[1.1.100,1.2.0)",
+ org.eclipse.jst.jsp.core;bundle-version="[1.2.300,1.3.0)",
+ org.eclipse.wst.html.core;bundle-version="[1.1.400,1.2.0)",
+ org.eclipse.wst.css.core;bundle-version="[1.1.300,1.2.0)",
+ org.eclipse.wst.xml.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.wst.sse.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.jdt.ui;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.jdt.core;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.debug.core;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.jdt.debug;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.search;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ltk.core.refactoring;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.common.uriresolver;bundle-version="[1.1.301,1.2.0)",
+ org.eclipse.ui.ide;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.views;bundle-version="[3.4.0,4.0.0)",
+ org.eclipse.wst.validation;bundle-version="[1.2.100,1.3.0)",
+ org.eclipse.core.filesystem;bundle-version="[1.3.0,2.0.0)"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/contentassist/JSPTaglibCompletionProposalComputer.java b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/contentassist/JSPTaglibCompletionProposalComputer.java
new file mode 100644
index 0000000..0f3797e
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/contentassist/JSPTaglibCompletionProposalComputer.java
@@ -0,0 +1,433 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jst.jsp.ui.internal.contentassist;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jst.jsp.core.internal.provisional.JSP11Namespace;
+import org.eclipse.jst.jsp.core.internal.util.FacetModuleCoreSupport;
+import org.eclipse.jst.jsp.core.taglib.IJarRecord;
+import org.eclipse.jst.jsp.core.taglib.ITLDRecord;
+import org.eclipse.jst.jsp.core.taglib.ITagDirRecord;
+import org.eclipse.jst.jsp.core.taglib.ITaglibDescriptor;
+import org.eclipse.jst.jsp.core.taglib.ITaglibRecord;
+import org.eclipse.jst.jsp.core.taglib.IURLRecord;
+import org.eclipse.jst.jsp.core.taglib.TaglibIndex;
+import org.eclipse.jst.jsp.ui.internal.JSPUIPlugin;
+import org.eclipse.jst.jsp.ui.internal.Logger;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.wst.common.uriresolver.internal.util.URIHelper;
+import org.eclipse.wst.html.core.internal.contentmodel.JSP20Namespace;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
+import org.eclipse.wst.sse.core.utils.StringUtils;
+import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext;
+import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal;
+import org.eclipse.wst.sse.ui.internal.contentassist.IRelevanceConstants;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;
+import org.eclipse.wst.xml.ui.internal.contentassist.DefaultXMLCompletionProposalComputer;
+import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImageHelper;
+import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImages;
+import org.w3c.dom.Node;
+
+/**
+ * <p>Compute JSP taglib completion proposals</p>
+ */
+public class JSPTaglibCompletionProposalComputer extends
+		DefaultXMLCompletionProposalComputer {
+
+	/**
+	 * @see org.eclipse.wst.xml.ui.internal.contentassist.DefaultXMLCompletionProposalComputer#addAttributeValueProposals(org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest, org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext)
+	 */
+	protected void addAttributeValueProposals(
+			ContentAssistRequest contentAssistRequest,
+			CompletionProposalInvocationContext context) {
+		
+		IPath basePath = getBasePath(contentAssistRequest);
+		if (basePath != null) {
+			IDOMNode node = (IDOMNode) contentAssistRequest.getNode();
+	
+			//only add attribute value proposals for specific elements
+			if(node.getNodeName().equals(JSP11Namespace.ElementName.DIRECTIVE_TAGLIB)) {
+				// Find the attribute name for which this position should have a value
+				IStructuredDocumentRegion open = node.getFirstStructuredDocumentRegion();
+				ITextRegionList openRegions = open.getRegions();
+				int i = openRegions.indexOf(contentAssistRequest.getRegion());
+				if (i < 0)
+					return;
+				ITextRegion nameRegion = null;
+				while (i >= 0) {
+					nameRegion = openRegions.get(i--);
+					if (nameRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)
+						break;
+				}
+		
+				String attributeName = null;
+				if (nameRegion != null)
+					attributeName = open.getText(nameRegion);
+		
+				String currentValue = null;
+				if (contentAssistRequest.getRegion().getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)
+					currentValue = contentAssistRequest.getText();
+				else
+					currentValue = ""; //$NON-NLS-1$
+				String matchString = null;
+				// fixups
+				int start = contentAssistRequest.getReplacementBeginPosition();
+				int length = contentAssistRequest.getReplacementLength();
+				if (currentValue.length() > StringUtils.strip(currentValue).length() &&
+						(currentValue.startsWith("\"") || currentValue.startsWith("'")) && //$NON-NLS-1$ //$NON-NLS-2$
+						contentAssistRequest.getMatchString().length() > 0) {
+					
+					matchString = currentValue.substring(1, contentAssistRequest.getMatchString().length());
+				}
+				else {
+					matchString = currentValue.substring(0, contentAssistRequest.getMatchString().length());
+				}
+				boolean existingComplicatedValue = contentAssistRequest.getRegion() != null &&
+						contentAssistRequest.getRegion() instanceof ITextRegionContainer;
+				if (existingComplicatedValue) {
+					contentAssistRequest.getProposals().clear();
+					contentAssistRequest.getMacros().clear();
+				}
+				else {
+					String lowerCaseMatch = matchString.toLowerCase(Locale.US);
+					if (attributeName.equals(JSP11Namespace.ATTR_NAME_URI)) {
+						ITaglibRecord[] availableTaglibRecords = TaglibIndex.getAvailableTaglibRecords(basePath);
+						/*
+						 * a simple enough way to remove duplicates (resolution at
+						 * runtime would be nondeterministic anyway)
+						 */
+						Map uriToRecords = new HashMap();
+						for (int taglibRecordNumber = 0; taglibRecordNumber < availableTaglibRecords.length; taglibRecordNumber++) {
+							ITaglibRecord taglibRecord = availableTaglibRecords[taglibRecordNumber];
+							ITaglibDescriptor descriptor = taglibRecord.getDescriptor();
+							String uri = null;
+							switch (taglibRecord.getRecordType()) {
+								case ITaglibRecord.URL :
+									uri = descriptor.getURI();
+									uriToRecords.put(uri, taglibRecord);
+									break;
+								case ITaglibRecord.JAR : {
+									IPath location = ((IJarRecord) taglibRecord).getLocation();
+									IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(location);
+									IPath localContextRoot = FacetModuleCoreSupport.computeWebContentRootPath(basePath);
+									for (int fileNumber = 0; fileNumber < files.length; fileNumber++) {
+										if (localContextRoot.isPrefixOf(files[fileNumber].getFullPath())) {
+											uri = IPath.SEPARATOR +
+													files[fileNumber].getFullPath().removeFirstSegments(localContextRoot.segmentCount()).toString();
+											uriToRecords.put(uri, taglibRecord);
+										}
+										else {
+											uri = FacetModuleCoreSupport.getRuntimePath(files[fileNumber].getFullPath()).toString();
+											uriToRecords.put(uri, taglibRecord);
+										}
+									}
+									break;
+								}
+								case ITaglibRecord.TLD : {
+									uri = descriptor.getURI();
+									if (uri == null || uri.trim().length() == 0) {
+										IPath path = ((ITLDRecord) taglibRecord).getPath();
+										IPath localContextRoot = FacetModuleCoreSupport.computeWebContentRootPath(basePath);
+										if (localContextRoot.isPrefixOf(path)) {
+											uri = IPath.SEPARATOR + path.removeFirstSegments(localContextRoot.segmentCount()).toString();
+										}
+										else {
+											uri = FacetModuleCoreSupport.getRuntimePath(path).toString();
+										}
+									}
+									uriToRecords.put(uri, taglibRecord);
+									break;
+								}
+							}
+						}
+						/*
+						 * use the records and their descriptors to construct
+						 * proposals
+						 */
+						Object[] uris = uriToRecords.keySet().toArray();
+						for (int uriNumber = 0; uriNumber < uris.length; uriNumber++) {
+							String uri = uris[uriNumber].toString();
+							ITaglibRecord taglibRecord = (ITaglibRecord) uriToRecords.get(uri);
+							ITaglibDescriptor descriptor = (taglibRecord).getDescriptor();
+							if (uri != null && uri.length() > 0 && (matchString.length() == 0 ||
+									uri.toLowerCase(Locale.US).startsWith(lowerCaseMatch))) {
+								
+								String url = getSmallImageURL(taglibRecord);
+								ImageDescriptor imageDescriptor = JSPUIPlugin.getInstance().getImageRegistry().getDescriptor(url);
+								if (imageDescriptor == null && url != null) {
+									URL imageURL;
+									try {
+										imageURL = new URL(url);
+										imageDescriptor = ImageDescriptor.createFromURL(imageURL);
+										JSPUIPlugin.getInstance().getImageRegistry().put(url, imageDescriptor);
+									}
+									catch (MalformedURLException e) {
+										Logger.logException(e);
+									}
+								}
+								String additionalInfo = descriptor.getDisplayName() + "<br/>" + //$NON-NLS-1$
+										descriptor.getDescription() + "<br/>" + descriptor.getTlibVersion(); //$NON-NLS-1$
+								Image image = null;
+								try {
+									image = JSPUIPlugin.getInstance().getImageRegistry().get(url);
+								}
+								catch (Exception e) {
+									Logger.logException(e);
+								}
+								if (image == null) {
+									image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ATTRIBUTE);
+								}
+								CustomCompletionProposal proposal = new CustomCompletionProposal(
+										"\"" + uri + "\"", start, length, uri.length() + 2, //$NON-NLS-1$ //$NON-NLS-2$
+										image, uri, null, additionalInfo, IRelevanceConstants.R_NONE);
+								contentAssistRequest.addProposal(proposal);
+							}
+						}
+					}
+					else if (attributeName.equals(JSP20Namespace.ATTR_NAME_TAGDIR)) {
+						ITaglibRecord[] availableTaglibRecords = TaglibIndex.getAvailableTaglibRecords(basePath);
+						/*
+						 * a simple enough way to remove duplicates (resolution at
+						 * runtime would be nondeterministic anyway)
+						 */
+						Map uriToRecords = new HashMap();
+						IPath localContextRoot = FacetModuleCoreSupport.computeWebContentRootPath(basePath);
+						for (int taglibRecordNumber = 0; taglibRecordNumber < availableTaglibRecords.length; taglibRecordNumber++) {
+							ITaglibRecord taglibRecord = availableTaglibRecords[taglibRecordNumber];
+							String uri = null;
+							if (taglibRecord.getRecordType() == ITaglibRecord.TAGDIR) {
+								IPath path = ((ITagDirRecord) taglibRecord).getPath();
+								if (localContextRoot.isPrefixOf(path)) {
+									uri = IPath.SEPARATOR + path.removeFirstSegments(localContextRoot.segmentCount()).toString();
+									uriToRecords.put(uri, taglibRecord);
+								}
+							}
+						}
+						/*
+						 * use the records and their descriptors to construct
+						 * proposals
+						 */
+						Object[] uris = uriToRecords.keySet().toArray();
+						for (int uriNumber = 0; uriNumber < uris.length; uriNumber++) {
+							String uri = uris[uriNumber].toString();
+							ITaglibRecord taglibRecord = (ITaglibRecord) uriToRecords.get(uri);
+							ITaglibDescriptor descriptor = (taglibRecord).getDescriptor();
+							if (uri != null && uri.length() > 0 && (matchString.length() == 0 ||
+									uri.toLowerCase(Locale.US).startsWith(lowerCaseMatch))) {
+								
+								String url = getSmallImageURL(taglibRecord);
+								ImageDescriptor imageDescriptor = null;
+								if (url != null) {
+									imageDescriptor = JSPUIPlugin.getInstance().getImageRegistry().getDescriptor(url);
+								}
+								if (imageDescriptor == null && url != null) {
+									URL imageURL;
+									try {
+										imageURL = new URL(url);
+										imageDescriptor = ImageDescriptor.createFromURL(imageURL);
+										JSPUIPlugin.getInstance().getImageRegistry().put(url, imageDescriptor);
+									}
+									catch (MalformedURLException e) {
+										Logger.logException(e);
+									}
+								}
+								String additionalInfo = descriptor.getDescription() + "<br/>" + descriptor.getTlibVersion(); //$NON-NLS-1$
+								Image image = null;
+								try {
+									image = JSPUIPlugin.getInstance().getImageRegistry().get(url);
+								}
+								catch (Exception e) {
+									Logger.logException(e);
+								}
+								if (image == null) {
+									image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ATTRIBUTE);
+								}
+								CustomCompletionProposal proposal = new CustomCompletionProposal(
+										"\"" + uri + "\"", start, length, uri.length() + 2, image, uri, //$NON-NLS-1$ //$NON-NLS-2$
+										null, additionalInfo, IRelevanceConstants.R_NONE);
+								contentAssistRequest.addProposal(proposal);
+							}
+						}
+					}
+					else if (attributeName.equals(JSP11Namespace.ATTR_NAME_PREFIX)) {
+						Node uriAttr = node.getAttributes().getNamedItem(JSP11Namespace.ATTR_NAME_URI);
+						String uri = null;
+						if (uriAttr != null) {
+							uri = uriAttr.getNodeValue();
+							ITaglibRecord[] availableTaglibRecords = TaglibIndex.getAvailableTaglibRecords(basePath);
+							Map prefixMap = new HashMap();
+							for (int taglibrecordNumber = 0; taglibrecordNumber < availableTaglibRecords.length; taglibrecordNumber++) {
+								ITaglibDescriptor descriptor = availableTaglibRecords[taglibrecordNumber].getDescriptor();
+								if (isTaglibForURI(uri, basePath, availableTaglibRecords[taglibrecordNumber])) {
+									String shortName = descriptor.getShortName().trim();
+									if (shortName.length() > 0) {
+										boolean valid = true;
+										for (int character = 0; character < shortName.length(); character++) {
+											valid = valid && !Character.isWhitespace(shortName.charAt(character));
+										}
+										if (valid) {
+											prefixMap.put(shortName, descriptor);
+										}
+									}
+								}
+							}
+							Object prefixes[] = prefixMap.keySet().toArray();
+							for (int j = 0; j < prefixes.length; j++) {
+								String prefix = (String) prefixes[j];
+								ITaglibDescriptor descriptor = (ITaglibDescriptor) prefixMap.get(prefix);
+								Image image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ATTRIBUTE);
+								CustomCompletionProposal proposal = new CustomCompletionProposal(
+										"\"" + prefix + "\"", start, length, prefix.length() + 2, image, //$NON-NLS-1$ //$NON-NLS-2$
+										prefix, null, descriptor.getDescription(), IRelevanceConstants.R_NONE);
+								contentAssistRequest.addProposal(proposal);
+							}
+						}
+						else {
+							Node dirAttr = node.getAttributes().getNamedItem(JSP20Namespace.ATTR_NAME_TAGDIR);
+							if (dirAttr != null) {
+								String dir = dirAttr.getNodeValue();
+								if (dir != null) {
+									ITaglibRecord record = TaglibIndex.resolve(basePath.toString(), dir, false);
+									if (record != null) {
+										ITaglibDescriptor descriptor = record.getDescriptor();
+										if (descriptor != null) {
+											String shortName = descriptor.getShortName();
+		
+											Image image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ATTRIBUTE);
+											CustomCompletionProposal proposal = new CustomCompletionProposal(
+													"\"" + shortName + "\"", start, length, shortName.length() + 2, //$NON-NLS-1$ //$NON-NLS-2$
+													image, shortName, null, descriptor.getDescription(),
+													IRelevanceConstants.R_NONE);
+											contentAssistRequest.addProposal(proposal);
+										}
+										else {
+											if (dir.startsWith("/WEB-INF/")) { //$NON-NLS-1$
+												dir = dir.substring(9);
+											}
+											String prefix = StringUtils.replace(dir, "/", "-"); //$NON-NLS-1$ //$NON-NLS-2$
+		
+											Image image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ATTRIBUTE);
+											CustomCompletionProposal proposal = new CustomCompletionProposal(
+													"\"" + prefix + "\"", start, length, prefix.length() + 2, //$NON-NLS-1$ //$NON-NLS-2$
+													image, prefix, null, null, IRelevanceConstants.R_NONE);
+											contentAssistRequest.addProposal(proposal);
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	
+	private boolean isTaglibForURI(String uri, IPath basePath, ITaglibRecord record) {
+		final ITaglibDescriptor descriptor = record.getDescriptor();
+		boolean matches = false;
+		if (descriptor != null) {
+			if (record.getRecordType() == ITaglibRecord.TLD && (descriptor.getURI() == null || "".equals(descriptor.getURI().trim()))) {
+				matches = ((ITLDRecord) record).getPath().equals(FacetModuleCoreSupport.resolve(basePath, uri));
+			}
+			else {
+				matches = descriptor.getURI().toLowerCase(Locale.US).equals(uri.toLowerCase(Locale.US));
+			}
+		}
+		return matches;
+	}
+
+	private String getSmallImageURL(ITaglibRecord taglibRecord) {
+		String url = null;
+		switch (taglibRecord.getRecordType()) {
+			case (ITaglibRecord.TLD) : {
+				ITLDRecord record = (ITLDRecord) taglibRecord;
+				IResource file = ResourcesPlugin.getWorkspace().getRoot().getFile(record.getPath());
+				if (file.getLocation() != null && record.getDescriptor().getSmallIcon().length() > 0) {
+					url = "platform:/resource/" + //$NON-NLS-1$
+					FacetModuleCoreSupport.resolve(file.getFullPath(),
+							record.getDescriptor().getSmallIcon());
+				}
+			}
+				break;
+			case (ITaglibRecord.JAR) : {
+				IJarRecord record = (IJarRecord) taglibRecord;
+				if (record.getDescriptor().getSmallIcon().length() > 0) {
+					// url = "file:" +
+					// URIHelper.normalize(record.getDescriptor().getSmallIcon(),
+					// record.getLocation().toString(), "/"); //$NON-NLS-1$
+				}
+			}
+				break;
+			case (ITaglibRecord.TAGDIR) : {
+			}
+				break;
+			case (ITaglibRecord.URL) : {
+				IURLRecord record = (IURLRecord) taglibRecord;
+				if (record.getDescriptor().getSmallIcon().length() > 0) {
+					url = URIHelper.normalize(record.getDescriptor().getSmallIcon(), record.getURL().toString(), "/"); //$NON-NLS-1$
+				}
+			}
+				break;
+		}
+		return url;
+	}
+	
+	/**
+	 * Returns project request is in
+	 * 
+	 * @param request
+	 * @return
+	 */
+	private IPath getBasePath(ContentAssistRequest request) {
+		IPath baselocation = null;
+
+		if (request != null) {
+			IStructuredDocumentRegion region = request.getDocumentRegion();
+			if (region != null) {
+				IDocument document = region.getParentDocument();
+				IStructuredModel model = null;
+				try {
+					model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+					if (model != null) {
+						String location = model.getBaseLocation();
+						if (location != null) {
+							baselocation = new Path(location);
+						}
+					}
+				}
+				finally {
+					if (model != null)
+						model.releaseFromRead();
+				}
+			}
+		}
+		return baselocation;
+	}
+}
diff --git a/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/hyperlink/JSPJavaHyperlinkDetector.java b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/hyperlink/JSPJavaHyperlinkDetector.java
new file mode 100644
index 0000000..7b2ba67
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/hyperlink/JSPJavaHyperlinkDetector.java
@@ -0,0 +1,264 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jst.jsp.ui.internal.hyperlink;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.ILocalVariable;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.ISourceReference;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.jst.jsp.core.internal.java.IJSPTranslation;
+import org.eclipse.jst.jsp.core.internal.java.JSPTranslation;
+import org.eclipse.jst.jsp.core.internal.java.JSPTranslationAdapter;
+import org.eclipse.jst.jsp.ui.internal.Logger;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.util.URIResolver;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+
+/**
+ * Detects hyperlinks in JSP Java content
+ */
+public class JSPJavaHyperlinkDetector extends AbstractHyperlinkDetector {
+
+	private IHyperlink createHyperlink(IJavaElement element, IRegion region, IDocument document) {
+		IHyperlink link = null;
+		if (region != null) {
+			// open local variable in the JSP file...
+			boolean isInTranslationCU = false;
+			if (element instanceof ISourceReference) {
+				IFile file = null;
+				int jspOffset = 0;
+				IStructuredModel sModel = null;
+
+				// try to locate the file in the workspace
+				try {
+					sModel = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+					if (sModel != null) {
+						URIResolver resolver = sModel.getResolver();
+						if (resolver != null) {
+							String uriString = resolver.getFileBaseLocation();
+							file = getFile(uriString);
+						}
+					}
+				}
+				finally {
+					if (sModel != null)
+						sModel.releaseFromRead();
+				}
+
+				// get Java range, translate coordinate to JSP
+
+				try {
+					ISourceRange range = null;
+					IJSPTranslation jspTranslation = getJSPTranslation(document);
+					if (jspTranslation != null) {
+						// link to local variable definitions
+						if (element instanceof ILocalVariable) {
+							range = ((ILocalVariable) element).getNameRange();
+							Object cu = ((ILocalVariable) element).getAncestor(IJavaElement.COMPILATION_UNIT);
+							if (cu != null && cu.equals(jspTranslation.getCompilationUnit()))
+								isInTranslationCU = true;
+						}
+						// linking to fields of the same compilation unit
+						else if (element.getElementType() == IJavaElement.FIELD) {
+							Object cu = ((IField) element).getCompilationUnit();
+							if (cu != null && cu.equals(jspTranslation.getCompilationUnit())) {
+								range = ((ISourceReference) element).getSourceRange();
+								isInTranslationCU = true;
+							}
+						}
+						// linking to methods of the same compilation unit
+						else if (element.getElementType() == IJavaElement.METHOD) {
+							Object cu = ((IMethod) element).getCompilationUnit();
+							if (cu != null && cu.equals(jspTranslation.getCompilationUnit())) {
+								range = ((ISourceReference) element).getSourceRange();
+								isInTranslationCU = true;
+							}
+						}
+					}
+
+					if (range != null && file != null) {
+						jspOffset = jspTranslation.getJspOffset(range.getOffset());
+						if (jspOffset >= 0) {
+							link = new WorkspaceFileHyperlink(region, file, new Region(jspOffset, range.getLength()));
+						}
+					}
+				}
+				catch (JavaModelException jme) {
+					Logger.log(Logger.WARNING_DEBUG, jme.getMessage(), jme);
+				}
+			}
+			if (link == null && !isInTranslationCU) { // Don't try to open the translation CU
+				link = new JSPJavaHyperlink(region, element);
+			}
+		}
+		return link;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.hyperlink.IHyperlinkDetector#detectHyperlinks(org.eclipse.jface.text.ITextViewer,
+	 *      org.eclipse.jface.text.IRegion, boolean)
+	 */
+	public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) {
+		List hyperlinks = new ArrayList(0);
+
+		if (region != null && textViewer != null) {
+			IDocument document = textViewer.getDocument();
+
+			// check and make sure this is a valid Java type
+			JSPTranslation jspTranslation = getJSPTranslation(document);
+			if (jspTranslation != null) {
+				// check if we are in JSP Java content
+				int javaOffset = jspTranslation.getJavaOffset(region.getOffset());
+				if (javaOffset > -1) {
+					// check that we are not in indirect Java content (like
+					// included files)
+					if (!jspTranslation.isIndirect(javaOffset)) {
+						// get Java elements
+						IJavaElement[] elements = jspTranslation.getElementsFromJspRange(region.getOffset(), region.getOffset() + region.getLength());
+						if (elements != null && elements.length > 0) {
+							// create a JSPJavaHyperlink for each Java element
+							for (int i = 0; i < elements.length; ++i) {
+								IJavaElement element = elements[i];
+
+								// find hyperlink range for Java element
+								IRegion hyperlinkRegion = selectWord(document, region.getOffset());
+								IHyperlink link = createHyperlink(element, hyperlinkRegion, document);
+								if (link != null) {
+									hyperlinks.add(link);
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		if (hyperlinks.size() == 0)
+			return null;
+		return (IHyperlink[]) hyperlinks.toArray(new IHyperlink[0]);
+	}
+
+	/**
+	 * Returns an IFile from the given uri if possible, null if cannot find
+	 * file from uri.
+	 * 
+	 * @param fileString
+	 *            file system path
+	 * @return returns IFile if fileString exists in the workspace
+	 */
+	private IFile getFile(String fileString) {
+		IFile file = null;
+
+		if (fileString != null) {
+			Path filePath = new Path(fileString);
+			if (filePath.segmentCount() > 1 && ResourcesPlugin.getWorkspace().getRoot().getFile(filePath).exists()) {
+				return ResourcesPlugin.getWorkspace().getRoot().getFile(filePath);
+			}
+			IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(filePath);
+			for (int i = 0; i < files.length && file == null; i++)
+				if (files[i].exists())
+					file = files[i];
+		}
+
+		return file;
+	}
+
+	/**
+	 * Get JSP translation object
+	 * 
+	 * @return JSPTranslation if one exists, null otherwise
+	 */
+	private JSPTranslation getJSPTranslation(IDocument document) {
+		JSPTranslation translation = null;
+
+		IDOMModel xmlModel = null;
+		try {
+			xmlModel = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(document);
+			if (xmlModel != null) {
+				IDOMDocument xmlDoc = xmlModel.getDocument();
+				JSPTranslationAdapter adapter = (JSPTranslationAdapter) xmlDoc.getAdapterFor(IJSPTranslation.class);
+				if (adapter != null) {
+					translation = adapter.getJSPTranslation();
+				}
+			}
+		}
+		finally {
+			if (xmlModel != null)
+				xmlModel.releaseFromRead();
+		}
+		return translation;
+	}
+
+	/**
+	 * Java always selects word when defining region
+	 * 
+	 * @param document
+	 * @param anchor
+	 * @return IRegion
+	 */
+	private IRegion selectWord(IDocument document, int anchor) {
+
+		try {
+			int offset = anchor;
+			char c;
+
+			while (offset >= 0) {
+				c = document.getChar(offset);
+				if (!Character.isJavaIdentifierPart(c))
+					break;
+				--offset;
+			}
+
+			int start = offset;
+
+			offset = anchor;
+			int length = document.getLength();
+
+			while (offset < length) {
+				c = document.getChar(offset);
+				if (!Character.isJavaIdentifierPart(c))
+					break;
+				++offset;
+			}
+
+			int end = offset;
+
+			if (start == end)
+				return new Region(start, 0);
+
+			return new Region(start + 1, end - start - 1);
+
+		}
+		catch (BadLocationException x) {
+			return null;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.wst.html.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.html.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..6e4a716
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.core/META-INF/MANIFEST.MF
@@ -0,0 +1,40 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.wst.html.core; singleton:=true
+Bundle-Version: 1.1.501.qualifier
+Bundle-Activator: org.eclipse.wst.html.core.internal.HTMLCorePlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.wst.html.core.internal;x-internal:=true,
+ org.eclipse.wst.html.core.internal.cleanup;x-internal:=true,
+ org.eclipse.wst.html.core.internal.commentelement.handlers;x-internal:=true,
+ org.eclipse.wst.html.core.internal.contentmodel;x-internal:=true,
+ org.eclipse.wst.html.core.internal.contentmodel.chtml;x-internal:=true,
+ org.eclipse.wst.html.core.internal.contentmodel.ssi;x-internal:=true,
+ org.eclipse.wst.html.core.internal.contentproperties;x-internal:=true,
+ org.eclipse.wst.html.core.internal.contenttype;x-internal:=true,
+ org.eclipse.wst.html.core.internal.document;x-internal:=true,
+ org.eclipse.wst.html.core.internal.encoding;x-internal:=true,
+ org.eclipse.wst.html.core.internal.format;x-internal:=true,
+ org.eclipse.wst.html.core.internal.htmlcss;x-internal:=true,
+ org.eclipse.wst.html.core.internal.modelhandler;x-internal:=true,
+ org.eclipse.wst.html.core.internal.modelquery;x-internal:=true,
+ org.eclipse.wst.html.core.internal.preferences;x-internal:=true,
+ org.eclipse.wst.html.core.internal.provisional;x-internal:=true,
+ org.eclipse.wst.html.core.internal.provisional.contenttype;x-internal:=true,
+ org.eclipse.wst.html.core.internal.provisional.text;x-internal:=true,
+ org.eclipse.wst.html.core.internal.text;x-internal:=true,
+ org.eclipse.wst.html.core.internal.validate;x-internal:=true,
+ org.eclipse.wst.html.core.text
+Import-Package: com.ibm.icu.util; version="3.8"
+Require-Bundle: org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.common.uriresolver;bundle-version="[1.1.301,1.2.0)",
+ org.eclipse.wst.sse.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.wst.xml.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.wst.css.core;bundle-version="[1.1.400,1.2.0)",
+ org.eclipse.core.filebuffers;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.common.modulecore;bundle-version="[1.2.0,2.0.0)";resolution:=optional
+Bundle-ActivationPolicy: lazy;exclude:="org.eclipse.wst.html.core.internal.contenttype,org.eclipse.wst.html.core.internal.provisional.contenttype"
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/contentmodel/HTML5AttributeCollection.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/contentmodel/HTML5AttributeCollection.java
new file mode 100644
index 0000000..4fcc2b9
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/contentmodel/HTML5AttributeCollection.java
@@ -0,0 +1,1327 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.html.core.internal.contentmodel;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.eclipse.wst.html.core.internal.provisional.HTML40Namespace;
+import org.eclipse.wst.html.core.internal.provisional.HTML50Namespace;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMDataType;
+
+public class HTML5AttributeCollection extends AttributeCollection implements HTML50Namespace {
+	/** html5 core attribs */
+	private static final String[] CORE = {ATTR_NAME_ACCESSKEY, ATTR_NAME_CLASS, ATTR_NAME_CONTENT_EDITABLE, ATTR_NAME_CONTEXT_MENU, ATTR_NAME_DIR, ATTR_NAME_DRAGGABLE, ATTR_NAME_HIDDEN, ATTR_NAME_ID, ATTR_NAME_LANG, ATTR_NAME_SPELLCHECK,ATTR_NAME_STYLE, ATTR_NAME_TABINDEX, ATTR_NAME_TITLE};
+	/** events for HTML5. */
+	private static final String[] EVENTS = {ATTR_NAME_ONABORT, ATTR_NAME_ONBLUR, ATTR_NAME_ONCAN_PLAY, ATTR_NAME_ONCAN_PLAY_THROUGH, ATTR_NAME_ONCHANGE, ATTR_NAME_ONCLICK, ATTR_NAME_ONCONTEXT_MENU, ATTR_NAME_ONDBLCLICK, ATTR_NAME_ONDRAG, ATTR_NAME_ONDRAG_END, ATTR_NAME_ONDRAG_ENTER, ATTR_NAME_ONDRAG_LEAVE, 
+		ATTR_NAME_ONDRAG_OVER, ATTR_NAME_ONDRAG_START, ATTR_NAME_ONDROP, ATTR_NAME_ONDURATION_CHANGE, ATTR_NAME_ONEMPTIED, ATTR_NAME_ONENDED, ATTR_NAME_ONERROR, ATTR_NAME_ONFOCUS, ATTR_NAME_ONFORM_CHANGE, ATTR_NAME_ONFORM_INPUT, ATTR_NAME_ONINVALID,ATTR_NAME_ONKEYPRESS, ATTR_NAME_ONKEYDOWN, ATTR_NAME_ONKEYUP, 
+		ATTR_NAME_ONLOAD, ATTR_NAME_ONLOAD_START, ATTR_NAME_ONLOADED_DATA, ATTR_NAME_ONLOADED_METADATA, ATTR_NAME_ONMOUSEDOWN, ATTR_NAME_ONMOUSEUP, ATTR_NAME_ONMOUSEOVER, ATTR_NAME_ONMOUSEMOVE, ATTR_NAME_ONMOUSEOUT, ATTR_NAME_ONMOUSE_WHEEL, ATTR_NAME_ONPAUSE, ATTR_NAME_ONPLAY, ATTR_NAME_ONPLAYING, ATTR_NAME_ONPROGRESS,
+		ATTR_NAME_ONRATE_CHANGE, ATTR_NAME_ONREADY_STATE_CHANGE, ATTR_NAME_ONSCROLL, ATTR_NAME_ONSEEKED, ATTR_NAME_ONSEEKING, ATTR_NAME_ONSELECT, ATTR_NAME_ONSHOW, ATTR_NAME_ONSTALLED, ATTR_NAME_ONSUBMIT, ATTR_NAME_ONSUSPEND, ATTR_NAME_ONTIME_UPDATE, ATTR_NAME_ONVOLUME_UPDATE, ATTR_NAME_ONWAITING};
+
+	protected HTMLAttrDeclImpl create(String attrName) {
+		HTMLAttrDeclImpl attr = null;
+		HTMLCMDataTypeImpl atype = null;
+		if (attrName.equalsIgnoreCase(ATTR_NAME_AUTOFOCUS)) {
+			// (disabled (disabled) #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			// boolean attribute must have the same value as its name.
+			String[] values = {ATTR_NAME_AUTOFOCUS};
+			atype.setEnumValues(values);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_AUTOFOCUS, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_CONTENT_EDITABLE)) {
+			// (contenteditable (EMPTY|TRUE|FALSE|INHERIT) TRUE)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_EMPTY, ATTR_VALUE_TRUE, ATTR_VALUE_FALSE, ATTR_VALUE_INHERIT};
+			atype.setEnumValues(values);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_TRUE);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_CONTENT_EDITABLE, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_CHALLENGE)) {
+			// (challenge CDATA; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_CHALLENGE, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_CONTEXT_MENU)) {
+			// (contextmenu, CDATA, IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_CONTEXT_MENU, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_DRAGGABLE)) {
+			// (draggable (TRUE|FALSE|AUTO) TRUE)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_TRUE, ATTR_VALUE_FALSE, ATTR_VALUE_AUTO};
+			atype.setEnumValues(values);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_FALSE);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_DRAGGABLE, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_FORM)) {
+			// (form CDATA; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORM, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_KEYTYPE)) {
+			// (keytype CDATA; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_KEYTYPE, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_LOW)) {
+			atype = new HTMLCMDataTypeImpl(CMDataType.NUMBER);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_LOW, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_HIGH)) {
+			atype = new HTMLCMDataTypeImpl(CMDataType.NUMBER);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_HIGH, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_OPTIMUM)) {
+			atype = new HTMLCMDataTypeImpl(CMDataType.NUMBER);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_OPTIMUM, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_MIN)) {
+			atype = new HTMLCMDataTypeImpl(CMDataType.NUMBER);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_MIN, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_MAX)) {
+			atype = new HTMLCMDataTypeImpl(CMDataType.NUMBER);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_MAX, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_OPEN)) {
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			atype.setEnumValues(new String[] { ATTR_NAME_OPEN });
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_OPEN, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_PUBDATE)) {
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			atype.setEnumValues(new String[] { ATTR_NAME_PUBDATE });
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_PUBDATE, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_SPELLCHECK)) {
+			// (spellcheck (EMPTY|TRUE|FALSE) TRUE)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_EMPTY, ATTR_VALUE_TRUE, ATTR_VALUE_FALSE};
+			atype.setEnumValues(values);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_FALSE);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_SPELLCHECK, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONABORT)) {
+			// (onabort %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONABORT, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONCAN_PLAY)) {
+			// (oncanplay %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONCAN_PLAY, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONCAN_PLAY_THROUGH)) {
+			// (oncanplaythrough %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONCAN_PLAY_THROUGH, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONCHANGE)) {
+			// (onchange %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONCHANGE, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONCONTEXT_MENU)) {
+			// (onacontextmenu %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONCONTEXT_MENU, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONDRAG)) {
+			// (onadrag %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONDRAG, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONDRAG_END)) {
+			// (ondragend %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONDRAG_END, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONDRAG_ENTER)) {
+			// (ondragenter %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONDRAG_ENTER, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONDRAG_LEAVE)) {
+			// (ondragleave %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONDRAG_LEAVE, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONDRAG_OVER)) {
+			// (ondragover %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONDRAG_OVER, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONDRAG_START)) {
+			// (ondragstart %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONDRAG_START, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONDROP)) {
+			// (ondrop %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONDROP, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONDURATION_CHANGE)) {
+			// (ondurationchange %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONDURATION_CHANGE, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONEMPTIED)) {
+			// (onemptied %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONEMPTIED, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONENDED)) {
+			// (onended %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONENDED, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONERROR)) {
+			// (onerror %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONERROR, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONFOCUS)) {
+			// (onfocus %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONFOCUS, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONFORM_CHANGE)) {
+			// (onformchange %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONFORM_CHANGE, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONFORM_INPUT)) {
+			// (onforminput %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONFORM_INPUT, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONINPUT)) {
+			// (oninput %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONINPUT, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONINVALID)) {
+			// (oninvalid %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONINVALID, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONLOAD)) {
+			// (onload %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONLOAD, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONLOAD_START)) {
+			// (onloadstart %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONLOAD_START, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONLOADED_DATA)) {
+			// (onloadeddata %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONLOADED_DATA, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONLOADED_METADATA)) {
+			// (onloadedmetadata %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONLOADED_METADATA, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONMOUSE_WHEEL)) {
+			// (onmousewheel %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONMOUSE_WHEEL, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONPLAY)) {
+			// (onplay %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONPLAY, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONPLAYING)) {
+			// (onplaying %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONPLAYING, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONPAUSE)) {
+			// (onpause %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONPAUSE, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONPROGRESS)) {
+			// (onprogress %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONPROGRESS, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONRATE_CHANGE)) {
+			// (onratechange %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONRATE_CHANGE, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONREADY_STATE_CHANGE)) {
+			// (onreadystatechange %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONREADY_STATE_CHANGE, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONSCROLL)) {
+			// (onscroll %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONSCROLL, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONSEEKED)) {
+			// (onseeked %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONSEEKED, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONSEEKING)) {
+			// (onseeking %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONSEEKING, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONSELECT)) {
+			// (onselect %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONSELECT, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONSHOW)) {
+			// (onshow %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONSHOW, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONSTALLED)) {
+			// (onstalled %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONSTALLED, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONSUBMIT)) {
+			// (onsubmit %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONSUBMIT, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONSUSPEND)) {
+			// (onsuspend %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONSUSPEND, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONTIME_UPDATE)) {
+			// (ontimeupdate %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONTIME_UPDATE, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONVOLUME_UPDATE)) {
+			// (onvolumeupdate %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONVOLUME_UPDATE, atype, CMAttributeDeclaration.OPTIONAL);
+
+		}
+		else if (attrName.equalsIgnoreCase(ATTR_NAME_ONWAITING)) {
+			// (onwaiting %Script; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.SCRIPT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ONWAITING, atype, CMAttributeDeclaration.OPTIONAL);
+		}
+		else {
+			attr = super.create(attrName);
+		}
+		return attr;
+	}
+
+	public void getAttrs(CMNamedNodeMapImpl declarations) {
+		// %coreattrs;
+		getCore(declarations);
+		// %events;
+		getEvents(declarations);
+	}
+
+	public void getCore(CMNamedNodeMapImpl declarations) {
+		Iterator names = Arrays.asList(CORE).iterator();
+		getDeclarations(declarations, names);
+	}
+
+	public void getEvents(CMNamedNodeMapImpl declarations) {
+		Iterator names = Arrays.asList(EVENTS).iterator();
+		getDeclarations(declarations, names);
+	}
+	
+	public void createAttributeDeclarations(String elementName, CMNamedNodeMapImpl attributes) {
+		/* (type %InputType; TEXT) ... should be defined locally.
+		 * (name CDATA #IMPLIED)
+		 * (value CDATA #IMPLIED)
+		 * (checked (checked) #IMPLIED)
+		 * (disabled (disabled) #IMPLIED)
+		 * (readonly (readonly) #IMPLIED)
+		 * (size CDATA #IMPLIED) ... should be defined locally.
+		 * (maxlength NUMBER #IMPLIED)
+		 * (src %URI; #IMPLIED)
+		 * (alt CDATA #IMPLIED) ... should be defined locally.
+		 * (accept %ContentTypes; #IMPLIED)
+		 * (width CDATA; #IMPLIED)
+		 * (height CDATA; #IMPLIED)
+		 * (autocomplete Boolean; #IMPLIED)
+		 * (autofocus Boolean; #IMPLIED)
+		 * (form CDATA; #IMPLIED)
+		 * (formaction)
+		 * (formenctype)
+		 * (formmethod)
+		 * (formnovalidate)
+		 * (formtarget)
+		 * (list)
+		 * (max)
+		 * (min)
+		 * (multiple)
+		 * (pattern)
+		 * (placeholder CDATA #IMPLIED)
+		 * (required)
+		 * (step)
+		 * discouraged tags :- 
+		 * (usemap %URI; #IMPLIED)
+		 * (ismap (ismap) #IMPLIED)
+		 */
+		if (elementName.equals(HTML40Namespace.ElementName.INPUT)){
+			HTMLCMDataTypeImpl atype = null;
+			HTMLAttrDeclImpl attr = null;
+			// (type %InputType; TEXT) ... should be defined locally.
+			// NOTE: %InputType is ENUM;
+			// (text | password | checkbox | radio | submit | reset |
+			//  file | hidden | image | button
+			//  color| date | time | datetime | datetime-local | month | week| email| 
+			//  number | range | search | tel)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_TEXT, ATTR_VALUE_PASSWORD, ATTR_VALUE_CHECKBOX, ATTR_VALUE_RADIO, ATTR_VALUE_SUBMIT, ATTR_VALUE_RESET, ATTR_VALUE_FILE, ATTR_VALUE_HIDDEN, ATTR_VALUE_IMAGE, ATTR_VALUE_BUTTON,
+					 ATTR_VALUE_COLOR, ATTR_VALUE_DATE, ATTR_VALUE_DATETIME, ATTR_VALUE_DATETIME_LOCAL, ATTR_VALUE_EMAIL, ATTR_VALUE_MONTH, ATTR_VALUE_NUMBER_STRING, ATTR_VALUE_RANGE, ATTR_VALUE_SEARCH, ATTR_VALUE_TEL, ATTR_VALUE_TIME};
+			atype.setEnumValues(values);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_TEXT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_TYPE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_TYPE, attr);
+
+			// (size CDATA #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_SIZE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_SIZE, attr);
+
+			// (alt CDATA #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ALT, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_ALT, attr);
+			
+			
+			// (type %autocomeplete; ) ... should be defined locally.
+			// NOTE: %autocomeplete is ENUM;
+			// (on | off)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] autoCompleteValues = {ATTR_VALUE_ON, ATTR_VALUE_OFF};
+			atype.setEnumValues(autoCompleteValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_AUTOCOMPLETE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_AUTOCOMPLETE, attr);
+
+			
+			// (form CDATA #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORM, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORM, attr);
+			
+			
+			// (formaction URI #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.URI);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMACTION, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMACTION, attr);
+
+			
+			// (type %formmethod; GET) ... should be defined locally.
+			// NOTE: %formmethod is ENUM;
+			// (GET|POST|PUT|DELETE)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] formMethodValues = {ATTR_VALUE_GET, ATTR_VALUE_POST, ATTR_VALUE_PUT, ATTR_VALUE_DELETE};
+			atype.setEnumValues(formMethodValues);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_GET);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMMETHOD, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMMETHOD, attr);
+
+			// (type %formenctype; GET) ... should be defined locally.
+			// NOTE: %formenctype is ENUM;
+			// (application/x-www-form-urlencoded| multipart/form-data| text/plain)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] formEncTypeValues = {ATTR_VALUE_WWW_FORM_URLENCODED, ATTR_VALUE_FORM_DATA, ATTR_VALUE_PLAIN};
+			atype.setEnumValues(formEncTypeValues);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_WWW_FORM_URLENCODED);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMENCTYPE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMENCTYPE, attr);
+
+			// (formtarget BROWSEING CONTEXT #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.BROWSING_CONTEXT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMTARGET, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMTARGET, attr);
+			
+			// (formtnovalidate  #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			// boolean attribute must have the same value as its name.
+			String[] formNoValidateValues = {ATTR_NAME_FORMNOVALIDATE};
+			atype.setEnumValues(formNoValidateValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMNOVALIDATE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMNOVALIDATE, attr);
+
+		
+			// (list ID #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.ID);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_LIST, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_LIST, attr);
+
+			// (min CDATA #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_MIN, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_MIN, attr);
+
+			// (max CDATA #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_MAX, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_MAX, attr);
+
+			// (maxlength NUMBER #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.NUMBER);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_MAXLENGTH, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_MAXLENGTH, attr);
+
+			// (multiple  #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			// boolean attribute must have the same value as its name.
+			String[] multipleValues = {ATTR_NAME_MULTIPLE};
+			atype.setEnumValues(multipleValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_MULTIPLE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_MULTIPLE, attr);
+
+		
+			// (step CDATA #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_STEP, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_STEP, attr);
+
+			// (placeholder CDATA #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.TEXT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_PLACEHOLDER, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_PLACEHOLDER, attr);
+
+			// (pattern CDATA #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_PATTERN, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_PATTERN, attr);
+
+			// (required  #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			// boolean attribute must have the same value as its name.
+			String[] requiredValues = {ATTR_NAME_REQUIRED};
+			atype.setEnumValues(requiredValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_REQUIRED, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_REQUIRED, attr);
+
+			String[] names = {ATTR_NAME_NAME, ATTR_NAME_VALUE, ATTR_NAME_CHECKED, ATTR_NAME_DISABLED, ATTR_NAME_READONLY, ATTR_NAME_SIZE, ATTR_NAME_MAXLENGTH, ATTR_NAME_SRC, ATTR_NAME_ALT, ATTR_NAME_ACCEPT, //<<D215684
+						ATTR_NAME_WIDTH, ATTR_NAME_HEIGHT,			//<D215684
+				//html5
+						ATTR_NAME_AUTOFOCUS
+			};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+			//discouraged
+			// (ismap (ismap) #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] ismapValues = {ATTR_NAME_ISMAP};
+			atype.setEnumValues(ismapValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ISMAP, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_ISMAP, attr);
+			
+			// (usemap %URI; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.URI);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_USEMAP, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_USEMAP, attr);
+
+			
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+		}
+		/* (href %URI; #IMPLIED)
+		 * (hreflang %LanguageCode; #IMPLIED)
+		 * (type %ContentType; #IMPLIED): should be defined locally.
+		 * (rel %LinkTypes; #IMPLIED)
+		 * (media %MediaDesc; #IMPLIED)
+		 * // discouraged
+		 * (charset %Charset; #IMPLIED)
+		 * (rev %LinkTypes; #IMPLIED)
+		 * (target %FrameTarget; #IMPLIED)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.LINK)){
+			String[] names = { ATTR_NAME_TYPE, ATTR_NAME_HREF, ATTR_NAME_HREFLANG, ATTR_NAME_REL,  ATTR_NAME_MEDIA};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+
+			// (sizes %Pixels; #IMPLIED)
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(HTMLCMDataType.PIXELS);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_SIZES, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_SIZES, attr);
+			
+			//discouraged
+			// (charset %Charset; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.CHARSET);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_CHARSET, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_CHARSET, attr);
+			
+			// (rev %LinkTypes; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.LINK_TYPES);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_REV, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_REV, attr);
+			
+			// (target %FrameTarget; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.FRAME_TARGET);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_TARGET, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_TARGET, attr);
+			
+		}
+		/* (type %ContentType; #IMPLIED)
+		 * (href %URI; #IMPLIED)
+		 * (hreflang %LanguageCode; #IMPLIED)
+		 * (target %FrameTarget; #IMPLIED)
+		 * (rel %LinkTypes; #IMPLIED)
+		 * (media %media_desc; #IMPLIED
+		 * //discouraged
+		 * (charset %Charset; #IMPLIED)
+		 * (name CDATA #IMPLIED)
+		 * (rev %LinkTypes; #IMPLIED)
+		 * (directkey %Character; #IMPLIED)
+		 * (shape %Shape; rect)
+		 * (coords %Coords; #IMPLIED)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.A)){
+			String[] names = { ATTR_NAME_MEDIA, ATTR_NAME_TYPE, ATTR_NAME_HREF, ATTR_NAME_HREFLANG, ATTR_NAME_REL, ATTR_NAME_TARGET};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+			
+			
+			//discouraged
+			// (charset %Charset; #IMPLIED)
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(HTMLCMDataType.CHARSET);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_CHARSET, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_CHARSET, attr);
+			
+			// (rev %LinkTypes; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.LINK_TYPES);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_REV, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_REV, attr);
+			
+			// (directkey %Character; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.CHARACTER);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_DIRECTKEY, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_DIRECTKEY, attr);
+			
+			// (shape %Shape; rect): %Shape; is (rect|circle|poly|default).
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_RECT, ATTR_VALUE_CIRCLE, ATTR_VALUE_POLY, ATTR_VALUE_DEFAULT};
+			atype.setEnumValues(values);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_RECT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_SHAPE, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_SHAPE, attr);
+			
+			// (coords %Coords; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.COORDS);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_COORDS, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_COORDS, attr);
+			
+			// (name CDATA #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_NAME, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_NAME, attr);
+	
+		}
+		/*
+		 * (shape %Shape; rect)
+		 * (coords %Coords; #IMPLIED)
+		 * (href %URI; #IMPLIED)
+		 * (target %FrameTarget; #IMPLIED)
+		 * (alt %Text; #REQUIRED)
+		 * (media %media_desc; #IMPLIED)
+		 * (rel %LinkTypes; #IMPLIED)
+		 * (type %ContentType; #IMPLIED)
+		 * //disocuraged
+		 * (nohref (nohref) #IMPLIED)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.AREA)){
+			// (media %MediaDesc; #IMPLIED)
+			String[] names = {ATTR_NAME_TYPE, ATTR_NAME_MEDIA, ATTR_NAME_SHAPE, ATTR_NAME_COORDS, ATTR_NAME_HREF, ATTR_NAME_HREFLANG, ATTR_NAME_TARGET, ATTR_NAME_ALT, ATTR_NAME_REL};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+			
+			// (nohref (nohref) #IMPLIED)
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_NAME_NOHREF};
+			atype.setEnumValues(values);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_NOHREF, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_NOHREF, attr);
+		
+		}
+		/*
+		 *  %globalattrs;
+		 * (http-equiv NAME #IMPLIED)
+		 * (name NAME #IMPLIED) ... should be defined locally.
+		 * (content CDATA #REQUIRED)
+		 * (charset %Charset; #IMPLIED)
+		 *  //discouraged
+		 * (scheme CDATA #IMPLIED)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.META)){
+			// globalattrs;
+			getAttrs(attributes);
+
+			// (name NAME #IMPLIED) ... should be defined locally.
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(HTMLCMDataType.NAME);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_NAME, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_NAME, attr);
+
+			// (content CDATA #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_CONTENT, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_CONTENT, attr);
+			
+			String[] names = {ATTR_NAME_HTTP_EQUIV, ATTR_NAME_CHARSET};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+			
+			// discouraged
+			// (scheme CDATA #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_SCHEME, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_SCHEME, attr);
+			
+		}
+		/*
+		 * (src %URI; #REQUIRED): should be defined locally.
+		 * (alt %Text; #REQUIRED)
+		 * (usemap %URI; #IMPLIED)
+		 * (ismap (ismap) #IMPLIED)
+		 *  // discouraged
+		 * (longdesc %URI; #IMPLIED)
+		 * (name CDATA #IMPLIED)
+		 * (height %Length; #IMPLIED)
+		 * (width %Length; #IMPLIED)
+		 * (align %IAlign; #IMPLIED): should be defined locally.
+		 * (border %Pixels; #IMPLIED)
+		 * (hspace %Pixels; #IMPLIED)
+		 * (vspace %Pixels; #IMPLIED)
+		 * (mapfile %URI; #IMPLIED)
+	 
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.IMG)){
+			// (src %URI; #REQUIRED): should be defined locally.
+			HTMLCMDataTypeImpl atype = null;
+			HTMLAttrDeclImpl attr = null;
+			atype = new HTMLCMDataTypeImpl(CMDataType.URI);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_SRC, atype, CMAttributeDeclaration.REQUIRED);
+			attributes.putNamedItem(ATTR_NAME_SRC, attr);
+
+			String[] names = {ATTR_NAME_ALT, ATTR_NAME_LONGDESC, ATTR_NAME_NAME, ATTR_NAME_HEIGHT, ATTR_NAME_WIDTH, ATTR_NAME_USEMAP, ATTR_NAME_ISMAP, ATTR_NAME_BORDER, ATTR_NAME_HSPACE, ATTR_NAME_VSPACE, ATTR_NAME_MAPFILE};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+
+			// align (local); should be defined locally.
+			attr = AttributeCollection.createAlignForImage();
+			attributes.putNamedItem(ATTR_NAME_ALIGN, attr);
+		
+		}
+		/*
+		 * (name CDATA #REQUIRED) ... should be defined locally.
+		 * (value CDATA #IMPLIED)
+		 * global attributes
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.PARAM)){
+			
+			// (name CDATA #REQUIRED) ... should be defined locally.
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_NAME, atype, CMAttributeDeclaration.REQUIRED);
+			attributes.putNamedItem(ATTR_NAME_NAME, attr);
+			
+			// (value CDATA #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_VALUE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_VALUE, attr);
+			
+			// gloabl attrs
+			getAttrs(attributes);
+		}
+		/*
+		 * (autofocus Boolean; #IMPLIED)
+		 * (form CDATA; #IMPLIED)
+		 * (placeholder CDATA #IMPLIED)
+		 * (name CDATA #IMPLIED)
+		 * (rows NUMBER #REQUIRED)
+		 * (cols NUMBER #REQUIRED)
+		 * (disabled (disabled) #IMPLIED)
+		 * (readonly (readonly) #IMPLIED)
+		 * (maxlength NUMBER; #IMPLIED)
+		 * (wrap ENUM; #IMPLIED)
+		 *  //discouraged
+		 * (istyle CDATA #IMPLIED)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.TEXTAREA)){
+			String[] names = {ATTR_NAME_MAXLENGTH, ATTR_NAME_FORM, ATTR_NAME_AUTOFOCUS, ATTR_NAME_NAME, ATTR_NAME_ROWS, ATTR_NAME_COLS, ATTR_NAME_DISABLED, ATTR_NAME_READONLY};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+			
+			// (placeholder CDATA #IMPLIED) ... should be defined locally.
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(HTMLCMDataType.TEXT);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_PLACEHOLDER, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_PLACEHOLDER, attr);
+			
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_SOFT, ATTR_VALUE_HARD};
+			atype.setEnumValues(values);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_SOFT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_WRAP, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_WRAP, attr);
+			
+			// discouraged
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ISTYLE, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_ISTYLE, attr);
+		}
+		/*
+		 * (charset %Charset; #IMPLIED)
+		 * (type %ContentType; #REQUIRED) ... should be defined locally.
+		 * (asynch boolean #IMPLIED)
+		 * (src %URI; #IMPLIED)
+		 * (defer (defer) #IMPLIED)
+		 *  // discouraged
+		 * (language CDATA #IMPLIED)
+		 * (event CDATA #IMPLIED)
+		 * (for %URI; #IMPLIED)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.SCRIPT)){
+			String[] names = {ATTR_NAME_CHARSET,  ATTR_NAME_SRC, ATTR_NAME_DEFER};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+
+			// (type %ContentType; #REQUIRED) ... should be defined locally.
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(HTMLCMDataType.CONTENT_TYPE);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, "text/javascript"); //$NON-NLS-1$
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_TYPE, atype, CMAttributeDeclaration.REQUIRED);
+			attributes.putNamedItem(ATTR_NAME_TYPE, attr);
+			
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_NAME_ASYNC};
+			atype.setEnumValues(values);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_ASYNC, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_ASYNC, attr);
+			
+			
+			// discouraged
+			// (language %CDATA; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_LANGUAGE, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_LANGUAGE, attr);
+			
+			// (event CDATA #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_EVENT, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_EVENT, attr);
+			
+			// (for %URI; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.URI);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FOR, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_FOR, attr);
+		
+			getAttrs(attributes);
+		}
+		/*
+		 *  %attrs;
+		 * (type %ContentType; #REQUIRED) ... should be defined locally.
+		 * (media %MediaDesc; #IMPLIED)
+		 * (scoped boolean; #implied)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.STYLE)){
+			// %i18n;
+			getAttrs(attributes);
+
+			String[] names = {ATTR_NAME_MEDIA};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+			// (type %ContentType; #REQUIRED) ... should be defined locally.
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(HTMLCMDataType.CONTENT_TYPE);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, "text/css"); //$NON-NLS-1$
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_TYPE, atype, CMAttributeDeclaration.REQUIRED);
+			attributes.putNamedItem(ATTR_NAME_TYPE, attr);
+			
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_NAME_SCOPED};
+			atype.setEnumValues(values);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_SCOPED, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_SCOPED, attr);
+		}
+		/*
+		 * %reserved;
+		 * (name CDATA #IMPLIED)
+		 * (size NUMBER #IMPLIED) ... should be defined locally.
+		 * (multiple (multiple) #IMPLIED)
+		 * (disabled (disabled) #IMPLIED)
+		 * (autofocus Boolean; #IMPLIED)
+		 * (form CDATA; #IMPLIED)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.SELECT)){
+			// (size NUMBER #IMPLIED) ... should be defined locally.
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(CMDataType.NUMBER);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_SIZE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_SIZE, attr);
+
+			String[] names = {ATTR_NAME_FORM, ATTR_NAME_AUTOFOCUS,ATTR_NAME_NAME, ATTR_NAME_MULTIPLE, ATTR_NAME_DISABLED, ATTR_NAME_TABINDEX, ATTR_NAME_ONFOCUS, ATTR_NAME_ONBLUR, ATTR_NAME_ONCHANGE};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+		
+		}
+		/*
+		 * (value NUMBER #IMPLIED) ... should be defined locally.
+		 *  //discouraged
+		 *  (type %LIStyle; #IMPLIED) ... should be defined locally.
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.LI)){
+			// (type %LIStyle; #IMPLIED) ... should be defined locally.
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(HTMLCMDataType.LI_STYLE);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_TYPE, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_TYPE, attr);
+
+			// (value NUMBER #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.NUMBER);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_VALUE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_VALUE, attr);
+		
+		}
+		/*
+		 * (start NUMBER #IMPLIED)
+		 * (reversed BOOLEAN; IMPLIED)
+		 *   //discouraged
+		 * (type %OLStyle; #IMPLIED) ... should be defined locally.
+		 * (compact (compact) #IMPLIED)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.OL)){
+
+			String[] names = { ATTR_NAME_START};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+			
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_NAME_REVERSED};
+			atype.setEnumValues(values);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_REVERSED, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_REVERSED, attr);
+			
+			//discouraged 
+			// (type %OLStyle; #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.OL_STYLE);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_TYPE, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_TYPE, attr);
+			
+			// (compact (compact) #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] compactValues = {ATTR_NAME_COMPACT};
+			atype.setEnumValues(compactValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_COMPACT, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_COMPACT, attr);
+		}
+		/**
+		 * %attrs;
+		 * (src %URI; #IMPLIED)
+		 * (srcdoc %CONTENT_TYPE; #IMPLIED)
+		 * (seamless BOOLEAN; #IMPLIED)
+		 * (sandbox CDATA; #IMPLED)
+		 * (height %Length; #IMPLIED)
+		 * (width %Length; #IMPLIED)
+		 * (name CDATA #IMPLIED)
+		 * //discouraged
+		 * (longdesc %URI; #IMPLIED)
+		 * (frameborder (1|0) 1)
+		 * (marginwidth %Pixels; #IMPLIED)
+		 * (marginheight %Pixels; #IMPLIED)
+		 * (scrolling (yes|no|auto) auto)
+		 * (align %IAlign; #IMPLIED) ... should be defined locally.
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.IFRAME)){
+			// %attrs;
+			getAttrs(attributes);
+			
+			//srcdoc
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(HTMLCMDataType.CONTENT_TYPE);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_SRCDOC, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_SRCDOC, attr);
+
+			// (seamless (seamless) #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] compactValues = {ATTR_NAME_SEAMLESS};
+			atype.setEnumValues(compactValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_SEAMLESS, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_SEAMLESS, attr);
+		
+			//sandbox
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_SANDBOX, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_SANDBOX, attr);
+			
+			
+			String[] names = { ATTR_NAME_NAME, ATTR_NAME_SRC, ATTR_NAME_HEIGHT, ATTR_NAME_WIDTH};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+
+			//discouraged
+			// (marginwidth %Pixels; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.PIXELS);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_MARGINWIDTH, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_MARGINWIDTH, attr);
+			
+			// (marginheight %Pixels; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.PIXELS);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_MARGINHEIGHT, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_MARGINHEIGHT, attr);
+			
+			// (scrolling (yes|no|auto) auto)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_YES, ATTR_VALUE_NO, ATTR_VALUE_AUTO};
+			atype.setEnumValues(values);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_AUTO);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_SCROLLING, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_SCROLLING, attr);
+
+			// (frameborder (1|0) 1)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] frameValues = {ATTR_VALUE_1, ATTR_VALUE_0};
+			atype.setEnumValues(frameValues);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_1);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FRAMEBORDER, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_FRAMEBORDER, attr);
+			
+			// (longdesc %URI; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.URI);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_LONGDESC, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_LONGDESC, attr);
+			
+			
+			// align
+			attr = AttributeCollection.createAlignForImage();
+			if (attr != null)
+				attr.obsolete(true);
+				attributes.putNamedItem(ATTR_NAME_ALIGN, attr);
+		}
+		/*
+		 * (%attrs)
+		 * (manisfest %URI; #IMPLIED)
+		 * (xmlns %URI; #IMPLIED)
+		 * //discouraged
+		 * (version CDATA #FIXED '%HTML.Version;)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.HTML)){
+			// %attrs;
+			getAttrs(attributes);
+			// (manisfest %URI; #IMPLIED)
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(CMDataType.URI);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_MANIFEST, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_MANIFEST, attr);
+			
+			// (version CDATA #FIXED '%HTML.Version;)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_FIXED, ATTR_VALUE_VERSION_TRANSITIONAL);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_VERSION, atype, CMAttributeDeclaration.FIXED);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_VERSION, attr);
+			
+			// (xmlns CDATA #FIXED '%xmlns;)
+			atype = new HTMLCMDataTypeImpl(CMDataType.URI);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_XMLNS, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_XMLNS, attr);
+		}
+		/*
+		 * (type enum; (context | toolbar | list))
+		 * (label %Text; #IMPLIED)
+		 *  //discouraged
+		 * (compact (compact) #IMPLIED)
+	 	 */
+		else if (elementName.equals(HTML40Namespace.ElementName.MENU)){
+			// (type %menuType; list) ... should be defined locally is ENUM.
+			//  (context | toolbar | list)
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_CONTEXT, ATTR_VALUE_TOOLBAR, ATTR_VALUE_LIST};
+			atype.setEnumValues(values);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_LIST);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_TYPE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_TYPE, attr);
+
+			// (label %Text; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.TEXT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_LABEL, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_LABEL, attr);
+
+			// (compact (compact) #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] compactValues = {ATTR_NAME_COMPACT};
+			atype.setEnumValues(compactValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_COMPACT, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_COMPACT, attr);
+		}
+		/*
+		 * (type %button; TEXT) ... should be defined locally.
+		 * (name CDATA #IMPLIED)
+		 * (value CDATA #IMPLIED)
+		 * (disabled (disabled) #IMPLIED)
+		 * (autofocus Boolean; #IMPLIED)
+		 * (form CDATA; #IMPLIED)
+		 * (formaction)
+		 * (formenctype)
+		 * (formmethod)
+		 * (formnovalidate)
+		 * (formtarget)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.BUTTON)){
+			// (type (button|submit|reset) submit) ... should be defined locally.
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_BUTTON, ATTR_VALUE_SUBMIT, ATTR_VALUE_RESET};
+			atype.setEnumValues(values);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_TYPE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_TYPE, attr);
+			
+
+			String[] names = {ATTR_NAME_NAME, ATTR_NAME_VALUE, ATTR_NAME_DISABLED,
+				//html5
+						ATTR_NAME_AUTOFOCUS
+			};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+			
+			// (form CDATA #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORM, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORM, attr);
+			
+			
+			// (formaction URI #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(CMDataType.URI);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMACTION, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMACTION, attr);
+
+			
+			// (type %formmethod; GET) ... should be defined locally.
+			// NOTE: %formmethod is ENUM;
+			// (GET|POST|PUT|DELETE)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] formMethodValues = {ATTR_VALUE_GET, ATTR_VALUE_POST, ATTR_VALUE_PUT, ATTR_VALUE_DELETE};
+			atype.setEnumValues(formMethodValues);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_GET);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMMETHOD, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMMETHOD, attr);
+
+			// (type %formenctype; GET) ... should be defined locally.
+			// NOTE: %formenctype is ENUM;
+			// (application/x-www-form-urlencoded| multipart/form-data| text/plain)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] formEncTypeValues = {ATTR_VALUE_WWW_FORM_URLENCODED, ATTR_VALUE_FORM_DATA, ATTR_VALUE_PLAIN};
+			atype.setEnumValues(formEncTypeValues);
+			atype.setImpliedValue(CMDataType.IMPLIED_VALUE_DEFAULT, ATTR_VALUE_WWW_FORM_URLENCODED);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMENCTYPE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMENCTYPE, attr);
+
+			// (formtarget BROWSEING CONTEXT #IMPLIED) ... should be defined locally.
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.BROWSING_CONTEXT);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMTARGET, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMTARGET, attr);
+			
+			// (formtnovalidate  #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			// boolean attribute must have the same value as its name.
+			String[] formNoValidateValues = {ATTR_NAME_FORMNOVALIDATE};
+			atype.setEnumValues(formNoValidateValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FORMNOVALIDATE, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORMNOVALIDATE, attr);
+
+		}
+		/*
+		 * (name CDATA #IMPLIED)
+		 * (disabled (disabled) #IMPLIED)
+		 * (form CDATA; #IMPLIED)
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.FIELDSET)){
+			// (form CDATA #IMPLIED) ... should be defined locally.
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_FORM, atype, CMAttributeDeclaration.OPTIONAL);
+			attributes.putNamedItem(ATTR_NAME_FORM, attr);
+			
+			String[] names = {ATTR_NAME_NAME, ATTR_NAME_DISABLED };
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+				
+		}
+		/*
+		 * (summary %Text; #IMPLIED)
+		 *  //discouraged
+		 * %reserved;
+		 * (width %Length; #IMPLIED)
+		 * (border %Pixels; #IMPLIED)
+		 * (frame %TFrame; #IMPLIED)
+		 * (rules %TRules; #IMPLIED)
+		 * (cellspacing %Length; #IMPLIED)
+		 * (cellpadding %Length; #IMPLIED)
+		 * (align %TAlign; #IMPLIED)
+		 * (bgcolor %Color; #IMPLIED)
+		 * (datapagesize CDATA #IMPLIED)
+		 * (height %Pixels; #IMPLIED)
+		 * (background %URI; #IMPLIED)
+		 * (bordercolor %Color #IMPLIED) ... D205514
+		 */
+		else if (elementName.equals(HTML40Namespace.ElementName.TABLE)){
+			// %reserved;
+			// ... %reserved; is empty in the current DTD.
+
+			String[] names = {ATTR_NAME_SUMMARY};
+			getDeclarations(attributes, Arrays.asList(names).iterator());
+
+			// align (local)
+			HTMLCMDataTypeImpl atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] alignValues = {ATTR_VALUE_LEFT, ATTR_VALUE_CENTER, ATTR_VALUE_RIGHT};
+			atype.setEnumValues(alignValues);
+			HTMLAttrDeclImpl attr = new HTMLAttrDeclImpl(ATTR_NAME_ALIGN, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_ALIGN, attr);
+			
+			// (width %Length; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.LENGTH);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_WIDTH, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_WIDTH, attr);
+			
+			// (border %Pixels; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.PIXELS);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_BORDER, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_BORDER, attr);
+			
+			// (frame %TFrame; #IMPLIED)
+			// %TFrame; is
+			// (void|above|below|hsides|lhs|rhs|vsides|box|border).
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] values = {ATTR_VALUE_VOID, ATTR_VALUE_ABOVE, ATTR_VALUE_BELOW, ATTR_VALUE_HSIDES, ATTR_VALUE_LHS, ATTR_VALUE_RHS, ATTR_VALUE_VSIDES, ATTR_VALUE_BOX, ATTR_VALUE_BORDER};
+			atype.setEnumValues(values);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_FRAME, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_FRAME, attr);
+			
+			// (rules %TRules; #IMPLIED)
+			// %TRules; is (none | groups | rows | cols | all).
+			atype = new HTMLCMDataTypeImpl(CMDataType.ENUM);
+			String[] ruleValues = {ATTR_VALUE_NONE, ATTR_VALUE_GROUPS, ATTR_VALUE_ROWS, ATTR_VALUE_COLS, ATTR_VALUE_ALL};
+			atype.setEnumValues(ruleValues);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_RULES, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_RULES, attr);
+			
+			// (cellspacing %Length; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.LENGTH);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_CELLSPACING, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_CELLSPACING, attr);
+			
+			// (cellpadding %Length; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.LENGTH);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_CELLPADDING, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_CELLPADDING, attr);
+			
+			// (bgcolor %Color; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.COLOR);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_BGCOLOR, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_BGCOLOR, attr);
+			
+			// (datapagesize CDATA #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.CDATA);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_DATAPAGESIZE, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_DATAPAGESIZE, attr);
+			
+			// (height %Length; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.LENGTH);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_HEIGHT, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_HEIGHT, attr);
+			
+			// (background %URI; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(CMDataType.URI);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_BACKGROUND, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_BACKGROUND, attr);
+			
+			// (bordercolor, %Color; #IMPLIED)
+			atype = new HTMLCMDataTypeImpl(HTMLCMDataType.COLOR);
+			attr = new HTMLAttrDeclImpl(ATTR_NAME_BORDERCOLOR, atype, CMAttributeDeclaration.OPTIONAL);
+			attr.obsolete(true);
+			attributes.putNamedItem(ATTR_NAME_BORDERCOLOR, attr);
+			
+			
+		}
+	}
+	
+	public static String[] getGlobalAttributeList(){
+		return CORE;
+	}
+	
+	public static String[] getGlobalEventList(){
+		return EVENTS;
+	}
+}
diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/format/HTMLFormatter.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/format/HTMLFormatter.java
new file mode 100644
index 0000000..38e003f
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/format/HTMLFormatter.java
@@ -0,0 +1,859 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Genuitec - fix for bug #203252
+ *******************************************************************************/
+package org.eclipse.wst.html.core.internal.format;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.wst.html.core.internal.HTMLCorePlugin;
+import org.eclipse.wst.html.core.internal.preferences.HTMLCorePreferenceNames;
+import org.eclipse.wst.html.core.internal.provisional.HTMLCMProperties;
+import org.eclipse.wst.html.core.internal.provisional.HTMLFormatContraints;
+import org.eclipse.wst.sse.core.internal.format.IStructuredFormatContraints;
+import org.eclipse.wst.sse.core.internal.format.IStructuredFormatPreferences;
+import org.eclipse.wst.sse.core.internal.format.IStructuredFormatter;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
+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.provisional.document.IDOMElement;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.eclipse.wst.xml.core.internal.provisional.format.StructuredFormatPreferencesXML;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+public class HTMLFormatter implements IStructuredFormatter {
+
+	private static final String HTML_NAME = "html";//$NON-NLS-1$
+	private static final String HEAD_NAME = "head"; //$NON-NLS-1$
+	private static final String BODY_NAME = "BODY";//$NON-NLS-1$
+	// hidden jsp logic that should be removed when jsp formatter is created
+	private static final String JSP = "jsp";//$NON-NLS-1$
+
+	private HTMLFormattingUtil formattingUtil;
+
+	public HTMLFormatter() {
+		formattingUtil = new HTMLFormattingUtil();
+	}
+
+	/**
+	 */
+	protected void addWidth(HTMLFormatContraints contraints, int width) {
+		if (contraints == null)
+			return;
+		if (!splitLines() || getLineWidth() < 0)
+			return;
+
+		int availableWidth = contraints.getAvailableLineWidth() - width;
+		if (availableWidth < 0)
+			availableWidth = 0;
+		contraints.setAvailableLineWidth(availableWidth);
+	}
+
+	/**
+	 */
+	protected boolean canFormatChild(Node node) {
+		while (node != null) {
+			if (node.getNodeType() != Node.ELEMENT_NODE)
+				return true;
+			CMElementDeclaration decl = getElementDeclaration((Element) node);
+			if (decl != null) {
+				if (decl.getContentType() == CMElementDeclaration.CDATA)
+					return false;
+				if (decl.supports(HTMLCMProperties.SHOULD_KEEP_SPACE)) {
+					boolean shouldKeepSpace = ((Boolean) decl.getProperty(HTMLCMProperties.SHOULD_KEEP_SPACE)).booleanValue();
+					if (shouldKeepSpace)
+						return false;
+				}
+			}
+			node = node.getParentNode();
+		}
+		return false;
+	}
+
+	/**
+	 */
+	protected boolean canInsertBreakAfter(CMElementDeclaration decl) {
+		if (decl == null)
+			return false;
+		if (!decl.supports(HTMLCMProperties.LINE_BREAK_HINT))
+			return false;
+		String hint = (String) decl.getProperty(HTMLCMProperties.LINE_BREAK_HINT);
+		if (hint == null)
+			return false;
+		return (hint.equals(HTMLCMProperties.Values.BREAK_BEFORE_START_AND_AFTER_END) || hint.equals(HTMLCMProperties.Values.BREAK_AFTER_START));
+	}
+
+	/**
+	 */
+	protected boolean canInsertBreakAfter(Node node) {
+		if (node == null)
+			return false;
+		Node parent = node.getParentNode();
+		if (parent == null)
+			return false;
+		Node next = node.getNextSibling();
+
+		// special exception if this node is a non-HTML tag (like JSP
+		// elements)
+		// BUG188093 - only preserve whitespace for jsp (not custom) tags
+		String prefix = node.getPrefix();
+		if (prefix != null && JSP.equals(prefix)) {
+			boolean canInsertBreakAfter = false;
+			// if a whitespace does not exist after it, do not add one
+			if (next != null && next.getNodeType() == Node.TEXT_NODE) {
+				String theText = ((Text) next).getData();
+				if (theText != null && theText.length() > 0) {
+					char theChar = theText.charAt(0);
+					canInsertBreakAfter = Character.isWhitespace(theChar);
+				}
+			}
+			// if cannot insert break, go ahead and return false (otherwise,
+			// continue processing)
+			if (!canInsertBreakAfter)
+				return false;
+		}
+
+		// special exception if next node is a non-HTML tag (like JSP
+		// elements)
+		// BUG188093 - only preserve whitespace for jsp (not custom) tags
+		if (next != null) {
+			prefix = next.getPrefix();
+			if (prefix != null && JSP.equals(prefix)) {
+				boolean canInsertBreakAfterPrevious = false;
+				// if a whitespace does not exist before it, do not add one
+				if (node.getNodeType() == Node.TEXT_NODE) {
+					String theText = ((Text) node).getData();
+					if (theText != null && theText.length() > 0) {
+						char theChar = theText.charAt(theText.length() - 1);
+						canInsertBreakAfterPrevious = Character.isWhitespace(theChar);
+					}
+				}
+				// if cannot insert break, go ahead and return false
+				// (otherwise,
+				// continue processing)
+				if (!canInsertBreakAfterPrevious)
+					return false;
+			}
+		}
+		if (parent.getNodeType() == Node.DOCUMENT_NODE) {
+			if (node.getNodeType() == Node.ELEMENT_NODE) {
+				// do not insert break after unclosed tag
+				if (!((IDOMElement) node).isClosed())
+					return false;
+			}
+			return true;
+		}
+		else if (parent.getNodeType() == Node.ELEMENT_NODE) {
+			IDOMElement element = (IDOMElement) parent;
+			// do not insert break before missing end tag
+			if (next == null && element.getEndStructuredDocumentRegion() == null)
+				return false;
+
+			// insert line break under non-HTML elements including JSP
+			// elements
+			if (element.getPrefix() != null)
+				return true;
+
+			CMElementDeclaration decl = getElementDeclaration(element);
+			if (decl != null) {
+				if (decl.getContentType() == CMElementDeclaration.ELEMENT)
+					return true;
+                // causes all closing tags to wrap to a new line
+                boolean allowsText = decl.getContentType() == CMElementDeclaration.MIXED
+                        || decl.getContentType() == CMElementDeclaration.PCDATA;
+                if (allowsNewlineAfter(allowsText, node, element))
+                    return true;
+				String tagName = element.getTagName();
+				// special for direct children under BODY
+				if (tagName != null && tagName.equalsIgnoreCase(BODY_NAME))
+					return true;
+			}
+		}
+
+		if (node.getNodeType() == Node.ELEMENT_NODE) {
+			IDOMElement element = (IDOMElement) node;
+			CMElementDeclaration decl = getElementDeclaration(element);
+			if (canInsertBreakAfter(decl)) {
+				// spcial for BR
+				return canFormatChild(parent);
+			}
+		}
+		if (next != null && next.getNodeType() == Node.ELEMENT_NODE) {
+			CMElementDeclaration decl = getElementDeclaration((Element) next);
+			if (canInsertBreakBefore(decl))
+				return true;
+		}
+		return false;
+	}
+
+	/**
+	 */
+	protected boolean canInsertBreakBefore(CMElementDeclaration decl) {
+		if (decl == null)
+			return false;
+		if (!decl.supports(HTMLCMProperties.LINE_BREAK_HINT))
+			return false;
+		String hint = (String) decl.getProperty(HTMLCMProperties.LINE_BREAK_HINT);
+		if (hint == null)
+			return false;
+		return hint.equals(HTMLCMProperties.Values.BREAK_BEFORE_START_AND_AFTER_END);
+	}
+
+	/**
+	 */
+	protected boolean canInsertBreakBefore(Node node) {
+		if (node == null)
+			return false;
+		Node parent = node.getParentNode();
+		if (parent == null)
+			return false;
+		Node prev = node.getPreviousSibling();
+
+		// special exception if this node is a non-HTML tag (like JSP
+		// elements)
+		// BUG188093 - only preserve whitespace for jsp (not custom) tags
+		String prefix = node.getPrefix();
+		if (prefix != null && JSP.equals(prefix)) {
+			boolean canInsertBreakBefore = false;
+			// if a whitespace does not exist before it, do not add one
+			if (prev != null && prev.getNodeType() == Node.TEXT_NODE) {
+				String theText = ((Text) prev).getData();
+				if (theText != null && theText.length() > 0) {
+					char theChar = theText.charAt(theText.length() - 1);
+					canInsertBreakBefore = Character.isWhitespace(theChar);
+				}
+			}
+			// if cannot insert break, go ahead and return false (otherwise,
+			// continue processing)
+			if (!canInsertBreakBefore)
+				return false;
+		}
+
+		// special exception if previous node is a non-HTML tag (like JSP
+		// elements)
+		// BUG188093 - only preserve whitespace for jsp (not custom) tags
+		if (prev != null) {
+			prefix = prev.getPrefix();
+			if (prefix != null && JSP.equals(prefix)) {
+				boolean canInsertBreakBeforeNext = false;
+				// if a whitespace does not exist after it, do not add one
+				if (node.getNodeType() == Node.TEXT_NODE) {
+					String theText = ((Text) node).getData();
+					if (theText != null && theText.length() > 0) {
+						char theChar = theText.charAt(0);
+						canInsertBreakBeforeNext = Character.isWhitespace(theChar);
+					}
+				}
+				// if cannot insert break, go ahead and return false
+				// (otherwise,
+				// continue processing)
+				if (!canInsertBreakBeforeNext)
+					return false;
+			}
+		}
+
+		if (parent.getNodeType() == Node.DOCUMENT_NODE) {
+			if (prev == null)
+				return false;
+			return true;
+		}
+		else if (parent.getNodeType() == Node.ELEMENT_NODE) {
+			IDOMElement element = (IDOMElement) parent;
+			// do not insert break after missing start tag
+			if (prev == null && element.getStartStructuredDocumentRegion() == null)
+				return false;
+
+			// insert line break under non-HTML elements including JSP
+			// elements
+			if (element.getPrefix() != null)
+				return true;
+
+			CMElementDeclaration decl = getElementDeclaration(element);
+			if (decl != null) {
+			    return allowNewlineBefore(node, element);
+			}
+		}
+
+		if (node.getNodeType() == Node.ELEMENT_NODE) {
+			return true;
+		}
+		if (prev != null && prev.getNodeType() == Node.ELEMENT_NODE) {
+			CMElementDeclaration decl = getElementDeclaration((Element) prev);
+			if (canInsertBreakAfter(decl)) {
+				// spcial for BR
+				return canFormatChild(parent);
+			}
+		}
+		return false;
+	}
+
+	/**
+	 */
+	public void format(Node node) {
+		format(node, getFormatContraints());
+	}
+
+	/**
+	 */
+	public void format(Node node, IStructuredFormatContraints contraints) {
+		if (node instanceof IDOMNode && contraints instanceof HTMLFormatContraints)
+			format((IDOMNode) node, (HTMLFormatContraints) contraints);
+	}
+
+	public void format(IDOMNode node, HTMLFormatContraints contraints) {
+		if (node == null)
+			return;
+		if (node.getParentNode() == null)
+			return; // do not format removed node
+
+		setWidth(contraints, node);
+
+		if (canInsertBreakBefore(node))
+			insertBreakBefore(node, contraints);
+
+		formatNode(node, contraints);
+
+		if (canInsertBreakAfter(node))
+			insertBreakAfter(node, contraints);
+	}
+
+	/**
+	 */
+	protected void formatChildNodes(IDOMNode node, HTMLFormatContraints contraints) {
+		if (node == null)
+			return;
+		if (!node.hasChildNodes())
+			return;
+
+		// concat adjacent texts
+		node.normalize();
+
+		// disable sibling indent during formatting all the children
+		boolean indent = false;
+		if (contraints != null) {
+			indent = contraints.getFormatWithSiblingIndent();
+			contraints.setFormatWithSiblingIndent(false);
+		}
+
+		boolean insertBreak = true;
+		IDOMNode child = (IDOMNode) node.getFirstChild();
+		while (child != null) {
+			if (child.getParentNode() != node)
+				break;
+			IDOMNode next = (IDOMNode) child.getNextSibling();
+
+			if (insertBreak && canInsertBreakBefore(child)) {
+				insertBreakBefore(child, contraints);
+			}
+
+			IStructuredFormatter formatter = HTMLFormatterFactory.getInstance().createFormatter(child, getFormatPreferences());
+			if (formatter != null) {
+				if (formatter instanceof HTMLFormatter) {
+					HTMLFormatter htmlFormatter = (HTMLFormatter) formatter;
+					htmlFormatter.formatNode(child, contraints);
+				}
+				else {
+					formatter.format(child);
+				}
+			}
+
+			if (canInsertBreakAfter(child)) {
+				insertBreakAfter(child, contraints);
+				insertBreak = false; // not to insert twice
+			}
+			else {
+				insertBreak = true;
+			}
+
+			child = next;
+		}
+
+		if (contraints != null)
+			contraints.setFormatWithSiblingIndent(indent);
+	}
+
+	/**
+	 */
+	protected void formatNode(IDOMNode node, HTMLFormatContraints contraints) {
+		if (node == null)
+			return;
+
+		if (node.hasChildNodes()) { // container
+			formatChildNodes(node, contraints);
+		}
+		else { // leaf
+			IStructuredDocumentRegion flatNode = node.getStartStructuredDocumentRegion();
+			if (flatNode != null) {
+				String source = flatNode.getText();
+				if (source != null && source.length() > 0) {
+					setWidth(contraints, source);
+				}
+			}
+		}
+	}
+
+	/**
+	 */
+	protected String getBreakSpaces(Node node) {
+		if (node == null)
+			return null;
+		StringBuffer buffer = new StringBuffer();
+
+		String delim = ((IDOMNode) node).getModel().getStructuredDocument().getLineDelimiter();
+		if (delim != null && delim.length() > 0)
+			buffer.append(delim);
+
+		String indent = getIndent();
+		if (indent != null && indent.length() > 0) {
+			for (Node parent = node.getParentNode(); parent != null; parent = parent.getParentNode()) {
+				if (parent.getNodeType() != Node.ELEMENT_NODE)
+					break;
+				// ignore omitted tag
+				if (((IDOMNode) parent).getStartStructuredDocumentRegion() == null)
+					continue;
+
+				IDOMElement element = (IDOMElement) parent;
+				if (element.getPrefix() != null) {
+					String localName = element.getLocalName();
+					// special for html:html
+					if (localName != null && !localName.equals(HTML_NAME)) {
+						buffer.append(indent);
+					}
+					continue;
+				}
+				else {
+					String localName = element.getLocalName();
+					if (HTML_NAME.equalsIgnoreCase(localName) || HEAD_NAME.equalsIgnoreCase(localName))
+						break;
+				}
+
+				CMElementDeclaration decl = getElementDeclaration(element);
+				if (decl != null && decl.supports(HTMLCMProperties.SHOULD_INDENT_CHILD_SOURCE)) {
+					boolean shouldIndent = isIdentable(node, parent); 
+					if (shouldIndent)
+						buffer.append(indent);
+				}
+
+			}
+		}
+
+		return buffer.toString();
+	}
+
+	/**
+	 */
+	protected String getIndent() {
+		return getFormatPreferences().getIndent();
+	}
+
+	/**
+	 */
+	protected int getLineWidth() {
+		return getFormatPreferences().getLineWidth();
+	}
+
+	/**
+	 */
+	protected CMElementDeclaration getElementDeclaration(Element element) {
+		if (element == null)
+			return null;
+		Document document = element.getOwnerDocument();
+		if (document == null)
+			return null;
+		ModelQuery modelQuery = ModelQueryUtil.getModelQuery(document);
+		if (modelQuery == null)
+			return null;
+		return modelQuery.getCMElementDeclaration(element);
+	}
+
+	/**
+	 * Checks if the content of an inline node has been formatted in any way
+	 * @param parent the parent node to check for formatting
+	 * @return true if the parent node is inline and also has text formatting
+	 */
+	protected boolean isContentFormatted(Node parent) {
+		final Node first = parent.getFirstChild();
+		if (first != null && first.getNodeType() == Node.TEXT_NODE) {
+			final String content = first.getNodeValue();
+			final int length = content.length();
+			for (int i = 0; i < length; i++) {
+				final char c = content.charAt(i);
+				if (c == '\r' || c == '\n') {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 */
+	protected void insertBreakAfter(IDOMNode node, HTMLFormatContraints contraints) {
+		if (node == null)
+			return;
+		if (node.getNodeType() == Node.TEXT_NODE)
+			return;
+		// don't insert break if node is on the last line
+		int documentLength = node.getStructuredDocument().getLength();
+		if (documentLength < 1 || (node.getEndOffset() >= (documentLength - 1)))
+			return;
+		Node parent = node.getParentNode();
+		if (parent == null)
+			return;
+		Node next = node.getNextSibling();
+
+		String spaces = null;
+		if (next == null) { // last spaces
+			// If the parent is inline and its content is not formatted don't insert
+			if (formattingUtil.isInline(parent) && !isContentFormatted(parent))
+				return;
+			// use parent indent for the end tag
+			spaces = getBreakSpaces(parent);
+		}
+		else if (next.getNodeType() == Node.TEXT_NODE) {
+			if (contraints != null && contraints.getFormatWithSiblingIndent()) {
+				IDOMNode text = (IDOMNode) next;
+				IStructuredFormatter formatter = HTMLFormatterFactory.getInstance().createFormatter(text, getFormatPreferences());
+				if (formatter instanceof HTMLTextFormatter) {
+					HTMLTextFormatter textFormatter = (HTMLTextFormatter) formatter;
+					textFormatter.formatText(text, contraints, HTMLTextFormatter.FORMAT_HEAD);
+				}
+			}
+			return;
+		}
+		else {
+			spaces = getBreakSpaces(node);
+		}
+		if (spaces == null || spaces.length() == 0)
+			return;
+
+		replaceSource(node.getModel(), node.getEndOffset(), 0, spaces);
+		setWidth(contraints, spaces);
+	}
+
+	/**
+	 */
+	protected void insertBreakBefore(IDOMNode node, HTMLFormatContraints contraints) {
+		if (node == null)
+			return;
+		if (node.getNodeType() == Node.TEXT_NODE)
+			return;
+		Node parent = node.getParentNode();
+		if (parent == null)
+			return;
+		Node prev = node.getPreviousSibling();
+
+		String spaces = null;
+		if (prev != null && prev.getNodeType() == Node.TEXT_NODE) {
+			if (contraints != null && contraints.getFormatWithSiblingIndent()) {
+				IDOMNode text = (IDOMNode) prev;
+				IStructuredFormatter formatter = HTMLFormatterFactory.getInstance().createFormatter(text, getFormatPreferences());
+				if (formatter instanceof HTMLTextFormatter) {
+					HTMLTextFormatter textFormatter = (HTMLTextFormatter) formatter;
+					textFormatter.formatText(text, contraints, HTMLTextFormatter.FORMAT_TAIL);
+				}
+			}
+			return;
+		}
+		else {
+			spaces = getBreakSpaces(node);
+		}
+		if (spaces == null || spaces.length() == 0)
+			return;
+
+		replaceSource(node.getModel(), node.getStartOffset(), 0, spaces);
+		setWidth(contraints, spaces);
+	}
+
+	/**
+	 */
+	protected boolean isWidthAvailable(HTMLFormatContraints contraints, int width) {
+		if (contraints == null)
+			return true;
+		if (!splitLines() || getLineWidth() < 0)
+			return true;
+		return (contraints.getAvailableLineWidth() >= width);
+	}
+
+	/**
+	 */
+	protected boolean keepBlankLines(HTMLFormatContraints contraints) {
+		if (contraints == null)
+			return true;
+		return (!contraints.getClearAllBlankLines());
+	}
+
+	/**
+	 */
+	protected void replaceSource(IStructuredDocumentRegion flatNode, int offset, int length, String source) {
+		if (flatNode == null)
+			return;
+		IStructuredDocument structuredDocument = flatNode.getParentDocument();
+		if (structuredDocument == null)
+			return;
+		if (source == null)
+			source = new String();
+		int startOffset = flatNode.getStartOffset();
+		if (structuredDocument.containsReadOnly(startOffset + offset, length))
+			return;
+		// We use 'structuredDocument' as the requester object just so this
+		// and the other
+		// format-related 'repalceText' (in replaceSource) can use the same
+		// requester.
+		// Otherwise, if requester is not identical,
+		// the undo group gets "broken" into multiple pieces based
+		// on the requesters being different. Technically, any unique, common
+		// requester object would work.
+		structuredDocument.replaceText(structuredDocument, startOffset + offset, length, source);
+	}
+
+	/**
+	 */
+	protected void replaceSource(IDOMModel model, int offset, int length, String source) {
+		if (model == null)
+			return;
+		IStructuredDocument structuredDocument = model.getStructuredDocument();
+		if (structuredDocument == null)
+			return;
+		if (source == null)
+			source = new String();
+		if (structuredDocument.containsReadOnly(offset, length))
+			return;
+		// We use 'structuredDocument' as the requester object just so this
+		// and the other
+		// format-related 'repalceText' (in replaceSource) can use the same
+		// requester.
+		// Otherwise, if requester is not identical,
+		// the undo group gets "broken" into multiple pieces based
+		// on the requesters being different. Technically, any unique, common
+		// requester object would work.
+		structuredDocument.replaceText(structuredDocument, offset, length, source);
+	}
+
+	/**
+	 */
+	protected void setWidth(HTMLFormatContraints contraints, String source) {
+		if (contraints == null)
+			return;
+		if (source == null)
+			return;
+		int length = source.length();
+		if (length == 0)
+			return;
+
+		if (!splitLines())
+			return;
+		int lineWidth = getLineWidth();
+		if (lineWidth < 0)
+			return;
+
+		int offset = source.lastIndexOf('\n');
+		int offset2 = source.lastIndexOf('\r');
+		if (offset2 > offset)
+			offset = offset2;
+		if (offset >= 0)
+			offset++;
+
+		int availableWidth = 0;
+		if (offset >= 0) {
+			availableWidth = lineWidth - (length - offset);
+		}
+		else {
+			availableWidth = contraints.getAvailableLineWidth() - length;
+		}
+		if (availableWidth < 0)
+			availableWidth = 0;
+		contraints.setAvailableLineWidth(availableWidth);
+	}
+
+	/**
+	 */
+	protected void setWidth(HTMLFormatContraints contraints, Node node) {
+		if (contraints == null)
+			return;
+		if (node == null)
+			return;
+		IStructuredDocument structuredDocument = ((IDOMNode) node).getStructuredDocument();
+		if (structuredDocument == null)
+			return; // error
+
+		if (!splitLines())
+			return;
+		int lineWidth = getLineWidth();
+		if (lineWidth < 0)
+			return;
+
+		int offset = ((IDOMNode) node).getStartOffset();
+		int line = structuredDocument.getLineOfOffset(offset);
+		int lineOffset = 0;
+		try {
+			lineOffset = structuredDocument.getLineOffset(line);
+		}
+		catch (BadLocationException ex) {
+			return; // error
+		}
+		if (lineOffset > offset)
+			return; // error
+
+		int availableWidth = lineWidth - (offset - lineOffset);
+		if (availableWidth < 0)
+			availableWidth = 0;
+
+		contraints.setAvailableLineWidth(availableWidth);
+	}
+
+	/**
+	 */
+	protected boolean splitLines() {
+		return true;// getFormatPreferences().getSplitLines();
+	}
+
+	protected IStructuredFormatPreferences fFormatPreferences = null;
+	protected HTMLFormatContraints fFormatContraints = null;
+	protected IProgressMonitor fProgressMonitor = null;
+
+	// public void format(XMLNode node, FormatContraints formatContraints) {
+	// if (formatContraints.getFormatWithSiblingIndent())
+	// formatContraints.setCurrentIndent(getSiblingIndent(node));
+	//
+	// formatNode(node, formatContraints);
+	// }
+
+	public void setFormatPreferences(IStructuredFormatPreferences formatPreferences) {
+		fFormatPreferences = formatPreferences;
+	}
+
+	public IStructuredFormatPreferences getFormatPreferences() {
+		if (fFormatPreferences == null) {
+			fFormatPreferences = new StructuredFormatPreferencesXML();
+
+			Preferences preferences = HTMLCorePlugin.getDefault().getPluginPreferences();
+			if (preferences != null) {
+				fFormatPreferences.setLineWidth(preferences.getInt(HTMLCorePreferenceNames.LINE_WIDTH));
+				((StructuredFormatPreferencesXML) fFormatPreferences).setSplitMultiAttrs(preferences.getBoolean(HTMLCorePreferenceNames.SPLIT_MULTI_ATTRS));
+				((StructuredFormatPreferencesXML) fFormatPreferences).setAlignEndBracket(preferences.getBoolean(HTMLCorePreferenceNames.ALIGN_END_BRACKET));
+				fFormatPreferences.setClearAllBlankLines(preferences.getBoolean(HTMLCorePreferenceNames.CLEAR_ALL_BLANK_LINES));
+
+				char indentChar = ' ';
+				String indentCharPref = preferences.getString(HTMLCorePreferenceNames.INDENTATION_CHAR);
+				if (HTMLCorePreferenceNames.TAB.equals(indentCharPref)) {
+					indentChar = '\t';
+				}
+				int indentationWidth = preferences.getInt(HTMLCorePreferenceNames.INDENTATION_SIZE);
+
+				StringBuffer indent = new StringBuffer();
+				for (int i = 0; i < indentationWidth; i++) {
+					indent.append(indentChar);
+				}
+				fFormatPreferences.setIndent(indent.toString());
+			}
+		}
+
+		return fFormatPreferences;
+	}
+
+	public IStructuredFormatContraints getFormatContraints() {
+		if (fFormatContraints == null) {
+			fFormatContraints = new HTMLFormatContraintsImpl();
+
+			fFormatContraints.setAvailableLineWidth(getFormatPreferences().getLineWidth());
+			fFormatContraints.setClearAllBlankLines(getFormatPreferences().getClearAllBlankLines());
+		}
+
+		return fFormatContraints;
+	}
+
+	public void setProgressMonitor(IProgressMonitor progressMonitor) {
+		fProgressMonitor = progressMonitor;
+	}
+
+    /* Check to see if current text Node is a child of an inline element. */
+    public boolean isInlinableTextNode(Node theNode, Element theParentElement) {
+        return formattingUtil.isInline(theParentElement) && 
+               theNode.getNodeType() == Node.TEXT_NODE;
+    }
+    
+    public boolean allowsNewlineAfter(boolean theBool, Node theNode, Element theParentElement) {
+        boolean result = theBool;
+        if ((theNode.getNodeType() == Node.TEXT_NODE) && formattingUtil.isInline(theParentElement)) {
+        	if (theParentElement.getChildNodes().getLength() == 1) { // Only child is a text node, no newline
+        		result = false;
+        	}
+        	else if (theNode == theParentElement.getLastChild()) { // theNode is the last, does formatting need corrected
+        		if (theParentElement.getFirstChild().getNodeType() == Node.ELEMENT_NODE) {
+        			result = false;
+        		}
+        	}
+        	else {
+        		result = false;
+        	}
+        } else if (theNode.getNodeType() == Node.ELEMENT_NODE
+                && formattingUtil.isInline(theNode.getNextSibling())) {
+            result = false;
+        }
+        else if (theNode.getNodeType() == Node.TEXT_NODE) {
+        	Node next = theNode.getNextSibling();
+        	if (next != null && formattingUtil.isInline(next) || theParentElement.getChildNodes().getLength() <= 1) {
+        		result = false;
+        	}
+        }
+        return result;
+    }
+
+    public boolean allowNewlineBefore(Node theNode) {
+        if (theNode.getNodeType() != Node.TEXT_NODE &&
+            theNode.getNodeType() != Node.ELEMENT_NODE) return false;
+        return (formattingUtil.isInline(theNode.getParentNode()) ||
+                        formattingUtil.isInline(theNode.getPreviousSibling()));        
+    }
+    
+    public boolean allowNewlineBefore(Node theNode, Element theParentElement) {
+        boolean result = true;
+        /* The calling method canInsertBreakBefore is checking if you can 
+         * insert a line break after the text node in the parentElement.  We 
+         * need to check for the case with inline element because we don't want to 
+         * break before the closing </tag> */
+        if (isInlinableTextNode(theNode, theParentElement)) {
+            result = false;
+        /* Check to see if we need to not break the line because we are
+         * a child of a inline element or a next sibling to an inline element*/
+        } else if (allowNewlineBefore(theNode)) {
+            result = false;
+        } else if (theNode.getNodeType() == Node.TEXT_NODE && theParentElement.getChildNodes().getLength() <= 1) {
+        	result = false;
+        }
+        return result;
+    }
+
+    public boolean isIdentable(Node theNode, Node theParent) {
+        boolean result = true;
+        /* The first 2 cases where we don't want to break/indent or if the
+         * node is a inlineText ELement or if we should skip it before its parent
+         * is an inlineText element.  
+         * The last check is to make sure that the parent is actually the parent
+         * of the node.  This method is called when the formatter is formatting
+         * the startTag and the wrap margin causes attributes to be indents on
+         * mulitple lines.  In this case where the parentNode doesn't match
+         * theParent argument, we can allow the indent. */
+        if (formattingUtil.isInline(theNode) && 
+        		formattingUtil.shouldSkipIndentForNode(theNode) &&
+                theParent == theNode.getParentNode()) {
+            result = false;
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.wst.html.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.html.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..275a6f3
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,52 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.wst.html.ui; singleton:=true
+Bundle-Version: 1.0.601.qualifier
+Bundle-Activator: org.eclipse.wst.html.ui.internal.HTMLUIPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.wst.html.internal.validation;x-internal:=true,
+ org.eclipse.wst.html.ui,
+ org.eclipse.wst.html.ui.internal;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.autoedit;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.contentassist;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.contentoutline;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.contentproperties.ui;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.correction;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.derived;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.edit.ui;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.editor;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.handlers;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.hyperlink;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.preferences;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.preferences.ui;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.registry;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.search;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.style;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.taginfo;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.templates;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.text;x-internal:=true,
+ org.eclipse.wst.html.ui.internal.wizard;x-internal:=true,
+ org.eclipse.wst.html.ui.views.contentoutline
+Import-Package: com.ibm.icu.util; version="3.8",
+ com.ibm.icu.text; version="3.8"
+Require-Bundle: org.eclipse.ui.ide;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.workbench.texteditor;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.forms;bundle-version="[3.4.0,4.0.0)",
+ org.eclipse.ui;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.sse.ui;bundle-version="[1.2.0,1.4.0)",
+ org.eclipse.wst.xml.ui;bundle-version="[1.1.100,1.2.0)",
+ org.eclipse.wst.css.ui;bundle-version="[1.0.500,1.1.0)",
+ org.eclipse.wst.sse.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.wst.css.core;bundle-version="[1.1.400,1.2.0)",
+ org.eclipse.wst.html.core;bundle-version="[1.1.400,1.2.0)",
+ org.eclipse.wst.xml.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.validation;bundle-version="[1.2.100,1.3.0)",
+ org.eclipse.wst.common.project.facet.core;bundle-version="[1.4.0,2.0.0)";resolution:=optional,
+ org.eclipse.wst.common.modulecore;bundle-version="[1.2.0,2.0.0)";resolution:=optional
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentproperties/ui/WebContentSettingsPropertyPage.java b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentproperties/ui/WebContentSettingsPropertyPage.java
new file mode 100644
index 0000000..cacd686
--- /dev/null
+++ b/bundles/org.eclipse.wst.html.ui/src/org/eclipse/wst/html/ui/internal/contentproperties/ui/WebContentSettingsPropertyPage.java
@@ -0,0 +1,412 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     David Schneider, david.schneider@unisys.com - [142500] WTP properties pages fonts don't follow Eclipse preferences
+ *******************************************************************************/
+package org.eclipse.wst.html.ui.internal.contentproperties.ui;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.PropertyPage;
+import org.eclipse.wst.css.core.internal.contentproperties.CSSContentProperties;
+import org.eclipse.wst.css.core.internal.metamodel.CSSProfile;
+import org.eclipse.wst.css.core.internal.metamodel.CSSProfileRegistry;
+import org.eclipse.wst.html.core.internal.contentproperties.HTMLContentProperties;
+import org.eclipse.wst.html.core.internal.document.HTMLDocumentTypeEntry;
+import org.eclipse.wst.html.core.internal.document.HTMLDocumentTypeRegistry;
+import org.eclipse.wst.html.ui.internal.HTMLUIMessages;
+import org.eclipse.wst.html.ui.internal.Logger;
+import org.eclipse.wst.html.ui.internal.editor.IHelpContextIds;
+
+public class WebContentSettingsPropertyPage extends PropertyPage {
+	private static final String SELECT_NONE = HTMLUIMessages.UI_none;
+	private String maxLengthStringInHTMLDocumentTypeRegistry = ""; //$NON-NLS-1$
+
+	private class ComboSelectionListener implements SelectionListener {
+		public void widgetDefaultSelected(SelectionEvent e) {
+			// do nothing
+		}
+
+		public void widgetSelected(SelectionEvent e) {
+			int index = fDocumentTypeCombo.getSelectionIndex();
+			String doctype = (String) fDocumentTypeIds.get(index);
+			updateDoctypeText(index, doctype);
+		}
+	}
+
+	Combo fDocumentTypeCombo;
+	List fDocumentTypeIds;
+	private Text fPublicIdText;
+	private Text fSystemIdText;
+	private Combo fProfileCombo;
+	private List fProfileIds;
+	private SelectionListener fListener;
+
+	public WebContentSettingsPropertyPage() {
+		super();
+		setDescription(HTMLUIMessages.WebContentSettingsPropertyPage_0);
+	}
+
+	private Composite createComposite(Composite parent, int numColumns) {
+		Composite composite = new Composite(parent, SWT.NULL);
+
+		// GridLayout
+		GridLayout layout = new GridLayout();
+		layout.numColumns = numColumns;
+		composite.setLayout(layout);
+
+		// GridData
+		GridData data = new GridData(GridData.FILL, GridData.FILL, true, false);
+		data.horizontalIndent = 0;
+		composite.setLayoutData(data);
+
+		return composite;
+	}
+
+	protected Control createContents(Composite parent) {
+		Composite propertyPage = createComposite(parent, 2);
+
+		createDoctypeContents(propertyPage);
+		createCSSProfileContents(propertyPage);
+
+		populateValues();
+		initializeValues();
+		computeMaxWidthHint();
+
+		fListener = new ComboSelectionListener();
+		fDocumentTypeCombo.addSelectionListener(fListener);
+
+		PlatformUI.getWorkbench().getHelpSystem().setHelp(propertyPage, IHelpContextIds.WEB_CONTENT_SETTINGS_HELPID);
+		Dialog.applyDialogFont(parent);
+		return propertyPage;
+	}
+
+	private void createCSSProfileContents(Composite parent) {
+		// CSS Profile
+		Label languageLabel = new Label(parent, SWT.NONE);
+		languageLabel.setText(HTMLUIMessages.UI_CSS_profile___2);
+		fProfileCombo = new Combo(parent, SWT.READ_ONLY);
+		GridData data = new GridData(GridData.FILL, GridData.FILL, true, false);
+		data.horizontalIndent = 0;
+		fProfileCombo.setLayoutData(data);
+	}
+
+	private void createDoctypeContents(Composite parent) {
+		// create description of implicit DOCTYPE
+		Text doctypeLabel = new Text(parent, SWT.READ_ONLY);
+		doctypeLabel.setText(HTMLUIMessages.UI_Description_of_role_of_following_DOCTYPE);
+		GridData data = new GridData(GridData.FILL, GridData.FILL, true, false);
+		data.horizontalIndent = 0;
+		data.horizontalSpan = 2;
+		doctypeLabel.setLayoutData(data);
+
+		// document type
+		Label languageLabel = new Label(parent, SWT.NONE);
+		languageLabel.setText(HTMLUIMessages.UI_Default_HTML_DOCTYPE_ID___1);
+		fDocumentTypeCombo = new Combo(parent, SWT.READ_ONLY);
+		data = new GridData(GridData.FILL, GridData.FILL, true, false);
+		data.horizontalIndent = 0;
+		fDocumentTypeCombo.setLayoutData(data);
+
+		// public ID
+		Label publicIdLabel = new Label(parent, SWT.NONE);
+		publicIdLabel.setText(HTMLUIMessages.UI_Public_ID);
+		fPublicIdText = new Text(parent, SWT.READ_ONLY | SWT.BORDER);
+		data = new GridData(GridData.FILL, GridData.FILL, true, false);
+		data.horizontalIndent = 0;
+		fPublicIdText.setLayoutData(data);
+
+		// system ID
+		Label systemIdLabel = new Label(parent, SWT.NONE);
+		systemIdLabel.setText(HTMLUIMessages.UI_System_ID);
+		fSystemIdText = new Text(parent, SWT.READ_ONLY | SWT.BORDER);
+		data = new GridData(GridData.FILL, GridData.FILL, true, false);
+		data.horizontalIndent = 0;
+		fSystemIdText.setLayoutData(data);
+
+		// create separator
+		Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
+		data = new GridData();
+		data.horizontalAlignment = GridData.FILL;
+		data.horizontalSpan = 2;
+		data.verticalSpan = 8;
+		label.setLayoutData(data);
+
+	}
+
+	/**
+	 * Get the resource this properties page is for
+	 * 
+	 * @return IResource for this properties page or null if there is no
+	 *         IResource
+	 */
+	private IResource getResource() {
+		IResource resource = null;
+		IAdaptable adaptable = getElement();
+		if (adaptable instanceof IResource) {
+			resource = (IResource) adaptable;
+		}
+		else if (adaptable != null) {
+			Object o = adaptable.getAdapter(IResource.class);
+			if (o instanceof IResource) {
+				resource = (IResource) o;
+			}
+		}
+		return resource;
+	}
+
+	private String getSystemIdFrom(String publicId) {
+		if (publicId == null || publicId.length() == 0)
+			return null;
+		HTMLDocumentTypeRegistry reg = HTMLDocumentTypeRegistry.getInstance();
+		Enumeration e = reg.getEntries();
+		while (e.hasMoreElements()) {
+			HTMLDocumentTypeEntry entry = (HTMLDocumentTypeEntry) e.nextElement();
+			if (entry.getPublicId().equals(publicId))
+				return entry.getSystemId();
+		}
+		return null;
+	}
+
+	private void initializeValues() {
+		initializeDoctypeValues();
+		initializeCSSProfileValues();
+	}
+
+	private void initializeCSSProfileValues() {
+		int index = 0;
+		String profile = CSSContentProperties.getProperty(CSSContentProperties.CSS_PROFILE, getResource(), false);
+		if (profile != null && profile.length() > 0) {
+			/*
+			 * If item is already part of combo, select it. Otherwise, select
+			 * none.
+			 */
+			index = fProfileIds.indexOf(profile);
+		}
+		index = index >= 0 ? index : 0;
+		fProfileCombo.select(index);
+	}
+
+	private void initializeDoctypeValues() {
+		int index = 0;
+		String doctype = HTMLContentProperties.getProperty(HTMLContentProperties.DOCUMENT_TYPE, getResource(), false);
+		if (doctype != null) {
+			/*
+			 * If item is already part of combo, select it. Otherwise, select
+			 * none.
+			 */
+			index = fDocumentTypeIds.indexOf(doctype);
+		}
+
+		// set combobox
+		index = index >= 0 ? index : 0;
+		fDocumentTypeCombo.select(index);
+
+		updateDoctypeText(index, doctype);
+	}
+
+	void updateDoctypeText(int index, String doctype) {
+		if (index > 0) {
+			// set public/system id text
+			fPublicIdText.setText(doctype);
+			String systemId = getSystemIdFrom(doctype);
+			if (systemId != null)
+				fSystemIdText.setText(systemId);
+			else
+				fSystemIdText.setText(""); //$NON-NLS-1$
+		}
+		else {
+			// set public/system id text
+			fPublicIdText.setText(""); //$NON-NLS-1$
+			fSystemIdText.setText(""); //$NON-NLS-1$
+		}
+	}
+
+	private void populateValues() {
+		populateDoctypeValues();
+		populateCSSProfileValues();
+	}
+
+	private void populateCSSProfileValues() {
+		fProfileIds = new ArrayList();
+		// add none first
+		fProfileCombo.add(SELECT_NONE);
+		fProfileIds.add(null);
+
+		CSSProfileRegistry reg = CSSProfileRegistry.getInstance();
+		Iterator i = reg.getProfiles();
+		while (i.hasNext()) {
+			CSSProfile profile = (CSSProfile) i.next();
+			String id = profile.getProfileID();
+			String name = profile.getProfileName();
+			fProfileCombo.add(name);
+			fProfileIds.add(id);
+		}
+	}
+
+	private void populateDoctypeValues() {
+		fDocumentTypeIds = new ArrayList();
+		// add none first
+		fDocumentTypeCombo.add(SELECT_NONE);
+		fDocumentTypeIds.add(null);
+
+		HTMLDocumentTypeRegistry reg = HTMLDocumentTypeRegistry.getInstance();
+		Enumeration e = reg.getEntries();
+		while (e.hasMoreElements()) {
+			HTMLDocumentTypeEntry entry = (HTMLDocumentTypeEntry) e.nextElement();
+			String publicId = entry.getPublicId();
+			String displayName = entry.getDisplayName();
+			displayName = displayName != null ? displayName : publicId;
+
+			fDocumentTypeCombo.add(displayName);
+			fDocumentTypeIds.add(publicId);
+
+			if (displayName.length() > maxLengthStringInHTMLDocumentTypeRegistry.length()) {
+				maxLengthStringInHTMLDocumentTypeRegistry = displayName;
+			}
+
+			if (entry.getSystemId() == null)
+				continue; // if HTML entry
+
+
+			if (entry.getSystemId().length() > maxLengthStringInHTMLDocumentTypeRegistry.length())
+				maxLengthStringInHTMLDocumentTypeRegistry = entry.getSystemId();
+		}
+	}
+
+	private void computeMaxWidthHint() {
+		// maxLengthString was set populateDoctypeValues was called
+		String maxLengthString = maxLengthStringInHTMLDocumentTypeRegistry;
+		String backup = fSystemIdText.getText();
+		fSystemIdText.setText(maxLengthString);
+		int maxWidthHint = fSystemIdText.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+		fSystemIdText.setText(backup);
+
+		if (fDocumentTypeCombo.getLayoutData() != null)
+			((GridData) fDocumentTypeCombo.getLayoutData()).widthHint = maxWidthHint;
+		if (fPublicIdText.getLayoutData() != null)
+			((GridData) fPublicIdText.getLayoutData()).widthHint = maxWidthHint;
+		if (fSystemIdText.getLayoutData() != null)
+			((GridData) fSystemIdText.getLayoutData()).widthHint = maxWidthHint;
+		if (fProfileCombo.getLayoutData() != null)
+			((GridData) fProfileCombo.getLayoutData()).widthHint = maxWidthHint;
+	}
+
+	private void performCSSProfileDefaults() {
+		int index = fProfileCombo.indexOf(SELECT_NONE);
+		if (index > -1)
+			fProfileCombo.select(index);
+
+		super.performDefaults();
+	}
+
+	private boolean performCSSProfileOk() {
+		int index = fProfileCombo.getSelectionIndex();
+		if (index > -1) {
+			String id = (String) fProfileIds.get(index);
+			if (id == null || id.length() == 0 || id.equalsIgnoreCase(SELECT_NONE)) {
+				// if none, use null
+				id = null;
+			}
+			try {
+				CSSContentProperties.setProperty(CSSContentProperties.CSS_PROFILE, getResource(), id);
+			}
+			catch (CoreException e) {
+				// maybe in future, let user know there was a problem saving
+				// file
+				Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
+				return false;
+			}
+		}
+		return true;
+	}
+
+	protected void performDefaults() {
+		super.performDefaults();
+
+		performDoctypeDefaults();
+		performCSSProfileDefaults();
+	}
+
+	private void performDoctypeDefaults() {
+		fPublicIdText.setText("");//$NON-NLS-1$
+		fSystemIdText.setText(""); //$NON-NLS-1$
+		int index = fDocumentTypeCombo.indexOf(SELECT_NONE);
+		if (index > -1)
+			fDocumentTypeCombo.select(index);
+	}
+
+	private boolean performDoctypeOk() {
+		int index = fDocumentTypeCombo.getSelectionIndex();
+		if (index > -1) {
+			String id = (String) fDocumentTypeIds.get(index);
+			if (id == null || id.equalsIgnoreCase(SELECT_NONE)) {
+				// if none, use null
+				id = null;
+			}
+			try {
+				HTMLContentProperties.setProperty(HTMLContentProperties.DOCUMENT_TYPE, getResource(), id);
+			}
+			catch (CoreException e) {
+				// maybe in future, let user know there was a problem saving
+				// file
+				Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
+				return false;
+			}
+		}
+		return true;
+	}
+
+	public boolean performOk() {
+		if (performDoctypeOk() || performCSSProfileOk()) {
+			// touch to mark for build-driven revalidation
+			IResource resource = getResource();
+			if (resource != null) {
+				try {
+					resource.accept(new IResourceVisitor() {
+
+						public boolean visit(IResource resource) throws CoreException {
+							try {
+								resource.touch(null);
+							}
+							catch (CoreException e) {
+								return false;
+							}
+							return true;
+						}
+					}, IResource.DEPTH_INFINITE, false);
+				}
+				catch (CoreException e) {
+					Logger.logException(e);
+				}
+			}
+		}
+
+		return super.performOk();
+	}
+}
diff --git a/bundles/org.eclipse.wst.jsdt.web.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.jsdt.web.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..6563794
--- /dev/null
+++ b/bundles/org.eclipse.wst.jsdt.web.core/META-INF/MANIFEST.MF
@@ -0,0 +1,35 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name.0
+Bundle-SymbolicName: org.eclipse.wst.jsdt.web.core;singleton:=true
+Bundle-Version: 1.0.401.qualifier
+Bundle-Activator: org.eclipse.wst.jsdt.web.core.internal.JsCorePlugin
+Bundle-Vendor: %Bundle-Vendor.0
+Bundle-Localization: plugin
+Export-Package: org.eclipse.wst.jsdt.web.core.internal;x-internal:=true,
+ org.eclipse.wst.jsdt.web.core.internal.modelhandler;x-internal:=true,
+ org.eclipse.wst.jsdt.web.core.internal.project;x-internal:=true;
+  uses:="org.eclipse.wst.common.project.facet.core,
+   org.eclipse.wst.jsdt.internal.core,
+   org.eclipse.wst.jsdt.core,
+   org.eclipse.core.resources,
+   org.eclipse.core.runtime",
+ org.eclipse.wst.jsdt.web.core.internal.provisional.contenttype;x-internal:=true,
+ org.eclipse.wst.jsdt.web.core.internal.validation;x-internal:=true,
+ org.eclipse.wst.jsdt.web.core.javascript,
+ org.eclipse.wst.jsdt.web.core.javascript.search,
+ org.eclipse.wst.jsdt.web.core.text
+Import-Package: com.ibm.icu.util; version="3.8"
+Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.filebuffers;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.sse.core;bundle-version="[1.1.504,1.2.0)",
+ org.eclipse.wst.xml.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.wst.html.core;bundle-version="[1.1.400,1.2.0)",
+ org.eclipse.wst.validation;bundle-version="[1.2.100,2.0.0)",
+ org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.jsdt.core;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.wst.common.project.facet.core;bundle-version="[1.4.0,2.0.0)";resolution:=optional,
+ org.eclipse.wst.common.modulecore;bundle-version="[1.1.0,2.0.0)";resolution:=optional
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Bundle-ActivationPolicy: lazy;exclude:="org.eclipse.wst.jsdt.web.core.internal.project"
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.jsdt.web.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..349427c
--- /dev/null
+++ b/bundles/org.eclipse.wst.jsdt.web.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,54 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name.0
+Bundle-SymbolicName: org.eclipse.wst.jsdt.web.ui; singleton:=true
+Bundle-Version: 1.0.401.qualifier
+Bundle-Activator: org.eclipse.wst.jsdt.web.ui.internal.JsUIPlugin
+Bundle-Vendor: %Bundle-Vendor.0
+Bundle-Localization: plugin
+Export-Package: org.eclipse.wst.jsdt.web.ui;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.contentassist,
+ org.eclipse.wst.jsdt.web.ui.internal;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.autoedit;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.contentassist;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.derived;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.editor;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.format;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.hyperlink;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.java.refactoring;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.java.search;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.java.search.ui;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.registry;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.style;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.style.java;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.taginfo;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.internal.text;x-internal:=true,
+ org.eclipse.wst.jsdt.web.ui.views.contentoutline
+Import-Package: com.ibm.icu.util; version="3.8",
+ com.ibm.icu.text; version="3.8",
+ org.eclipse.core.filesystem,
+ org.eclipse.ui.views.contentoutline
+Require-Bundle: org.eclipse.ui.ide;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.workbench;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.workbench.texteditor;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.sse.ui;bundle-version="[1.3.0,1.4.0)",
+ org.eclipse.wst.xml.ui;bundle-version="[1.1.100,1.2.0)",
+ org.eclipse.wst.jsdt.web.core;bundle-version="[1.0.300,2.0.0)",
+ org.eclipse.wst.css.core;bundle-version="[1.1.400,1.2.0)",
+ org.eclipse.wst.xml.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.wst.sse.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.debug.core;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.search;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ltk.core.refactoring;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.common.uriresolver;bundle-version="[1.1.300,1.2.0)",
+ org.eclipse.ui.ide;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.jsdt.core;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.wst.jsdt.ui;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.wst.html.ui;bundle-version="[1.0.500,1.1.0)",
+ org.eclipse.wst.html.core;bundle-version="[1.1.400,1.2.0)",
+ org.eclipse.wst.jsdt.manipulation;bundle-version="[1.0.200,2.0.0)"
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Bundle-ActivationPolicy: lazy
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/contentassist/JSDTContentAssistant.java b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/contentassist/JSDTContentAssistant.java
new file mode 100644
index 0000000..ee92dd9
--- /dev/null
+++ b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/contentassist/JSDTContentAssistant.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.jsdt.web.ui.internal.contentassist;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.wst.jsdt.core.CompletionProposal;
+import org.eclipse.wst.jsdt.web.core.javascript.IJsTranslation;
+import org.eclipse.wst.jsdt.web.core.javascript.JsTranslationAdapter;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext;
+import org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.eclipse.wst.xml.ui.internal.contentassist.AbstractContentAssistProcessor;
+/**
+*
+
+* Provisional API: This class/interface is part of an interim API that is still under development and expected to
+* change significantly before reaching stability. It is being made available at this early stage to solicit feedback
+* from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
+* (repeatedly) as the API evolves.
+*/
+public class JSDTContentAssistant extends AbstractContentAssistProcessor implements ICompletionProposalComputer {
+	private JSDTContentAssistantProcessor fContentAssistProcessor;
+	private JSDTTemplateAssistProcessor fTemplateAssistProcessor;
+	private JSDTHtmlCompletionProcessor fHhtmlcomp;
+	
+	public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentPosition) {
+		Vector proposals = new Vector();
+		ICompletionProposal[] completionProposals;
+		ICompletionProposal endScript = getHtmlContentAssistProcessor().getEndScriptProposal(viewer, documentPosition);
+		if(endScript!=null) {
+			return new ICompletionProposal[] {endScript};
+			//proposals.add(endScript);
+		}
+		JSDTProposalCollector theCollector = getProposalCollector(viewer, documentPosition);
+		/* add end script tag if needed */
+
+		/* --------- Content Assistant --------- */
+		if(theCollector==null) return new ICompletionProposal[0];
+		
+		getContentAssistProcessor().setProposalCollector(theCollector);
+		completionProposals = getContentAssistProcessor().computeCompletionProposals(viewer, documentPosition);
+		proposals.addAll(Arrays.asList(completionProposals));
+		/* HTML Proposals */
+		completionProposals = getHtmlContentAssistProcessor().computeCompletionProposals(viewer, documentPosition);
+		proposals.addAll(Arrays.asList(completionProposals));
+		/* --------- template completions --------- */
+		getTemplateCompletionProcessor().setProposalCollector(theCollector);
+		completionProposals = getTemplateCompletionProcessor().computeCompletionProposals(viewer, documentPosition);
+		proposals.addAll(Arrays.asList(completionProposals));
+		return (ICompletionProposal[]) proposals.toArray(new ICompletionProposal[0]);
+	}
+	
+	private JSDTHtmlCompletionProcessor getHtmlContentAssistProcessor() {
+		if (fHhtmlcomp == null) {
+			fHhtmlcomp = new JSDTHtmlCompletionProcessor();
+		}
+		return fHhtmlcomp;
+	}
+	
+	private JSDTContentAssistantProcessor getContentAssistProcessor() {
+		if (fContentAssistProcessor == null) {
+			fContentAssistProcessor = new JSDTContentAssistantProcessor();
+		}
+		return fContentAssistProcessor;
+	}
+	private IJsTranslation getJSPTranslation(ITextViewer viewer, int offset) {
+		IDOMModel xmlModel = null;
+		try {
+			xmlModel = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(viewer.getDocument());
+			IDOMDocument xmlDoc = xmlModel.getDocument();
+
+			JsTranslationAdapter translationAdapter = (JsTranslationAdapter) xmlDoc.getAdapterFor(IJsTranslation.class);
+			
+			if (translationAdapter != null) {
+				return translationAdapter.getJsTranslation(true);
+			}
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} finally {
+			if (xmlModel != null) {
+				xmlModel.releaseFromRead();
+			}
+		}
+		return null;
+	}
+	
+	protected JSDTProposalCollector getProposalCollector(ITextViewer viewer, int offset) {
+		IJsTranslation tran = getJSPTranslation(viewer, offset);
+		if(tran==null) return null;
+		return new JSDTProposalCollector(tran);
+	}
+	
+	private JSDTTemplateAssistProcessor getTemplateCompletionProcessor() {
+		if (fTemplateAssistProcessor == null) {
+			fTemplateAssistProcessor = new JSDTTemplateAssistProcessor();
+		}
+		return fTemplateAssistProcessor;
+	}
+
+	public void sessionStarted() {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public List computeCompletionProposals(
+			CompletionProposalInvocationContext context,
+			IProgressMonitor monitor) {
+		Vector proposals = new Vector();
+		ICompletionProposal[] completionProposals;
+		ICompletionProposal endScript = getHtmlContentAssistProcessor().getEndScriptProposal(context.getViewer(), context.getInvocationOffset());
+		if(endScript!=null) {
+			return new ArrayList(0);
+			//proposals.add(endScript);
+		}
+		JSDTProposalCollector theCollector = getProposalCollector(context.getViewer(), context.getInvocationOffset());
+		/* add end script tag if needed */
+		
+		theCollector.setAllowsRequiredProposals(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF, true);
+
+		/* --------- Content Assistant --------- */
+		if(theCollector==null) return new ArrayList(0);
+		
+		getContentAssistProcessor().setProposalCollector(theCollector);
+		completionProposals = getContentAssistProcessor().computeCompletionProposals(context.getViewer(), context.getInvocationOffset());
+		proposals.addAll(Arrays.asList(completionProposals));
+		/* HTML Proposals */
+		completionProposals = getHtmlContentAssistProcessor().computeCompletionProposals(context.getViewer(), context.getInvocationOffset());
+		proposals.addAll(Arrays.asList(completionProposals));
+		/* --------- template completions --------- */
+		getTemplateCompletionProcessor().setProposalCollector(theCollector);
+		completionProposals = getTemplateCompletionProcessor().computeCompletionProposals(context.getViewer(), context.getInvocationOffset());
+		proposals.addAll(Arrays.asList(completionProposals));
+		return proposals;
+	}
+
+	public List computeContextInformation(
+			CompletionProposalInvocationContext context,
+			IProgressMonitor monitor) {
+		return Arrays.asList(computeContextInformation(context.getViewer(), context.getInvocationOffset()));
+	}
+
+	public void sessionEnded() {
+		fContentAssistProcessor = null;
+		fTemplateAssistProcessor = null;
+		fHhtmlcomp = null;
+	}
+}
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/contentassist/JSDTProposalCollector.java b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/contentassist/JSDTProposalCollector.java
new file mode 100644
index 0000000..9f873f5
--- /dev/null
+++ b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/contentassist/JSDTProposalCollector.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.jsdt.web.ui.internal.contentassist;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.wst.jsdt.core.CompletionProposal;
+import org.eclipse.wst.jsdt.core.Signature;
+import org.eclipse.wst.jsdt.ui.text.java.CompletionProposalCollector;
+import org.eclipse.wst.jsdt.ui.text.java.CompletionProposalComparator;
+import org.eclipse.wst.jsdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.wst.jsdt.web.core.javascript.IJsTranslation;
+
+
+/**
+*
+
+* Provisional API: This class/interface is part of an interim API that is still under development and expected to
+* change significantly before reaching stability. It is being made available at this early stage to solicit feedback
+* from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
+* (repeatedly) as the API evolves.
+*/
+public class JSDTProposalCollector extends CompletionProposalCollector {
+	static char[] getTypeTriggers() {
+		return CompletionProposalCollector.TYPE_TRIGGERS;
+	}
+	private Comparator fComparator;
+	private IJsTranslation fTranslation;
+	
+// public List getGeneratedFunctionNames(){
+// if(fGeneratedFunctionNames==null){
+// fGeneratedFunctionNames = fTranslation.getGeneratedFunctionNames();
+// }
+// return fGeneratedFunctionNames;
+// }
+	public JSDTProposalCollector(IJsTranslation translation) {
+		super(translation!=null?translation.getCompilationUnit():null);
+		if (translation == null) {
+			throw new IllegalArgumentException("JSPTranslation cannot be null"); //$NON-NLS-1$
+		}
+		fTranslation = translation;
+	}
+	
+	/**
+	 * Cacluates the where the cursor should be after applying this proposal.
+	 * eg. method(|) if the method proposal chosen had params.
+	 * 
+	 * @param proposal
+	 * @param completion
+	 * @return
+	 */
+	private int calculatePositionAfter(CompletionProposal proposal, String completion) {
+		int positionAfter = completion.length();
+		int kind = proposal.getKind();
+		// may need better logic here...
+		// put cursor inside parenthesis if there's params
+		// only checking for any kind of declaration
+		if (kind == CompletionProposal.ANONYMOUS_CLASS_DECLARATION || kind == CompletionProposal.METHOD_DECLARATION || kind == CompletionProposal.POTENTIAL_METHOD_DECLARATION || kind == CompletionProposal.METHOD_REF) {
+			int paramCount = Signature.getParameterCount(proposal.getSignature());
+			if (completion.length() > 0 && paramCount > 0) {
+				positionAfter--;
+			}
+		}
+		return positionAfter;
+	}
+	
+	/**
+	 * Overridden to: - translate Java -> JSP offsets - fix
+	 * cursor-position-after - fix mangled servlet name in display string -
+	 * remove unwanted proposals (servlet constructor)
+	 */
+	
+	protected IJavaCompletionProposal createJavaCompletionProposal(CompletionProposal proposal) {
+		return createJSDTProposal(proposal);
+	}
+	
+	private JSDTCompletionProposal createJSDTProposal(CompletionProposal proposal) {
+		JSDTCompletionProposal jspProposal;
+		String completion = String.valueOf(proposal.getCompletion());
+		// java offset
+		int offset = proposal.getReplaceStart();
+		// replacement length
+		int length = proposal.getReplaceEnd() - offset;
+		// translate offset from Java > JSP
+		// cursor position after must be calculated
+		int positionAfter = calculatePositionAfter(proposal, completion);
+		// from java proposal
+		IJavaCompletionProposal javaProposal = super.createJavaCompletionProposal(proposal);
+		proposal.getDeclarationSignature();
+		Image image = javaProposal.getImage();
+		String displayString = javaProposal.getDisplayString();
+		displayString = getTranslation().fixupMangledName(displayString);
+
+		IContextInformation contextInformation = javaProposal.getContextInformation();
+		// String additionalInfo = javaProposal.getAdditionalProposalInfo();
+		int relevance = javaProposal.getRelevance();
+		boolean updateLengthOnValidate = true;
+		jspProposal = new JSDTCompletionProposal(completion, offset, length, positionAfter, image, displayString, contextInformation, null, relevance, updateLengthOnValidate);
+		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=124483
+		// set wrapped java proposal so additional info can be calculated on
+		// demand
+		jspProposal.setJavaCompletionProposal(javaProposal);
+		return jspProposal;
+	}
+	
+	private Comparator getComparator() {
+		if (fComparator == null) {
+			fComparator = new CompletionProposalComparator();
+		}
+		return fComparator;
+	}
+	
+	/**
+	 * Ensures that we only return JSPCompletionProposals.
+	 * 
+	 * @return an array of JSPCompletionProposals
+	 */
+	public JSDTCompletionProposal[] getJSPCompletionProposals() {
+		List results = new ArrayList();
+		IJavaCompletionProposal[] javaProposals = getJavaCompletionProposals();
+		// need to filter out non JSPCompletionProposals
+		// because their offsets haven't been translated
+		for (int i = 0; i < javaProposals.length; i++) {
+			if (javaProposals[i] instanceof JSDTCompletionProposal) {
+				results.add(javaProposals[i]);
+			}
+		}
+		Collections.sort(results, getComparator());
+		return (JSDTCompletionProposal[]) results.toArray(new JSDTCompletionProposal[results.size()]);
+	}
+	
+	public IJsTranslation getTranslation() {
+		return fTranslation;
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/hyperlink/JSDTHyperlink.java b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/hyperlink/JSDTHyperlink.java
new file mode 100644
index 0000000..2dbb3fd
--- /dev/null
+++ b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/hyperlink/JSDTHyperlink.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.jsdt.web.ui.internal.hyperlink;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.wst.html.ui.internal.HTMLUIMessages;
+import org.eclipse.wst.jsdt.core.IJavaScriptElement;
+import org.eclipse.wst.jsdt.internal.ui.actions.ActionMessages;
+import org.eclipse.wst.jsdt.ui.JavaScriptUI;
+import org.eclipse.wst.jsdt.web.ui.internal.Logger;
+/**
+*
+
+* Provisional API: This class/interface is part of an interim API that is still under development and expected to
+* change significantly before reaching stability. It is being made available at this early stage to solicit feedback
+* from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
+* (repeatedly) as the API evolves.
+*/
+class JSDTHyperlink implements IHyperlink {
+	private IJavaScriptElement fElement;
+	private IRegion fRegion;
+	
+	public JSDTHyperlink(IRegion region, IJavaScriptElement element) {
+		fRegion = region;
+		fElement = element;
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.hyperlink.IHyperlink#getHyperlinkRegion()
+	 */
+	public IRegion getHyperlinkRegion() {
+		return fRegion;
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.hyperlink.IHyperlink#getHyperlinkText()
+	 */
+	public String getHyperlinkText() {
+		return fElement != null ? NLS.bind(HTMLUIMessages.Open, fElement.getDisplayName()) : ActionMessages.OpenAction_declaration_label;
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.hyperlink.IHyperlink#getTypeLabel()
+	 */
+	public String getTypeLabel() {
+		return null;
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.hyperlink.IHyperlink#open()
+	 */
+	public void open() {
+		try {
+			IEditorPart editor = JavaScriptUI.openInEditor(fElement);
+			if (editor != null) {
+				JavaScriptUI.revealInEditor(editor, fElement);
+			}
+		} catch (Exception e) {
+			Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/StructuredModelManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/StructuredModelManager.java
new file mode 100644
index 0000000..61e0718
--- /dev/null
+++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/StructuredModelManager.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     
+ *******************************************************************************/
+package org.eclipse.wst.sse.core.internal.provisional;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.wst.sse.core.internal.SSECorePlugin;
+import org.eclipse.wst.sse.core.internal.model.ModelManagerImpl;
+import org.osgi.framework.Bundle;
+
+/**
+ * Class to allow access to ModelManager. Not intended to be subclassed.
+ * 
+ * @deprecated - use {@link org.eclipse.wst.sse.core.StructuredModelManager} instead
+ */
+final public class StructuredModelManager {
+	/**
+	 * Do not allow instances to be created.
+	 */
+	private StructuredModelManager() {
+		super();
+	}
+
+	/**
+	 * Provides access to the instance of IModelManager. Returns null if model
+	 * manager can not be created or is not valid (such as, when workbench is
+	 * shutting down).
+	 * 
+	 * @return IModelManager - returns the one model manager for structured
+	 *         model
+	 * @deprecated - use the one that is in
+	 *             org.eclipse.wst.sse.core.StructuredModelManager
+	 */
+	public static IModelManager getModelManager() {
+		boolean isReady = false;
+		IModelManager modelManager = null;
+		while (!isReady) {
+			Bundle localBundle = Platform.getBundle(SSECorePlugin.ID);
+			int state = localBundle.getState();
+			if (state == Bundle.ACTIVE) {
+				isReady = true;
+				// getInstance is a synchronized static method.
+				modelManager = ModelManagerImpl.getInstance();
+			}
+			else if (state == Bundle.STARTING) {
+				try {
+					Thread.sleep(100);
+				}
+				catch (InterruptedException e) {
+					// ignore, just loop again
+				}
+			}
+			else if (state == Bundle.STOPPING || state == Bundle.UNINSTALLED) {
+				isReady = true;
+				modelManager = null;
+			}
+			else {
+				// not sure about other states, 'resolved', 'installed'
+				isReady = true;
+				modelManager = null;
+			}
+		}
+		return modelManager;
+	}
+}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/BasicStructuredDocument.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/BasicStructuredDocument.java
new file mode 100644
index 0000000..c4821c1
--- /dev/null
+++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/BasicStructuredDocument.java
@@ -0,0 +1,2979 @@
+/*******************************************************************************
+ * Copyright (c) 2001, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Jens Lukowski/Innoopract - initial renaming/restructuring
+ *     Jesper Steen Møller - initial IDocumentExtension4 support - #102822
+ *                           (see also #239115)
+ *     David Carver (Intalio) - bug 300434 - Make inner classes static where possible
+ *     David Carver (Intalio) - bug 300443 - some constants aren't static final
+ *     
+ *******************************************************************************/
+package org.eclipse.wst.sse.core.internal.text;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPartitioningException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultLineTracker;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.DocumentPartitioningChangedEvent;
+import org.eclipse.jface.text.DocumentRewriteSession;
+import org.eclipse.jface.text.DocumentRewriteSessionEvent;
+import org.eclipse.jface.text.DocumentRewriteSessionType;
+import org.eclipse.jface.text.FindReplaceDocumentAdapter;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IDocumentExtension4;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.IDocumentPartitionerExtension;
+import org.eclipse.jface.text.IDocumentPartitionerExtension2;
+import org.eclipse.jface.text.IDocumentPartitionerExtension3;
+import org.eclipse.jface.text.IDocumentPartitioningListener;
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension;
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2;
+import org.eclipse.jface.text.IDocumentRewriteSessionListener;
+import org.eclipse.jface.text.ILineTracker;
+import org.eclipse.jface.text.ILineTrackerExtension;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextStore;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.TypedRegion;
+import org.eclipse.wst.sse.core.internal.Logger;
+import org.eclipse.wst.sse.core.internal.document.StructuredDocumentFactory;
+import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento;
+import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser;
+import org.eclipse.wst.sse.core.internal.provisional.events.AboutToBeChangedEvent;
+import org.eclipse.wst.sse.core.internal.provisional.events.IModelAboutToBeChangedListener;
+import org.eclipse.wst.sse.core.internal.provisional.events.IStructuredDocumentListener;
+import org.eclipse.wst.sse.core.internal.provisional.events.NoChangeEvent;
+import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent;
+import org.eclipse.wst.sse.core.internal.provisional.events.RegionsReplacedEvent;
+import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent;
+import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredPartitioning;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredTextReParser;
+import org.eclipse.wst.sse.core.internal.text.rules.StructuredTextPartitioner;
+import org.eclipse.wst.sse.core.internal.undo.IStructuredTextUndoManager;
+import org.eclipse.wst.sse.core.internal.undo.StructuredTextUndoManager;
+import org.eclipse.wst.sse.core.internal.util.Assert;
+import org.eclipse.wst.sse.core.internal.util.Debug;
+import org.eclipse.wst.sse.core.internal.util.Utilities;
+
+
+/**
+ * The standard implementation of structured document.
+ */
+public class BasicStructuredDocument implements IStructuredDocument, IDocumentExtension, IDocumentExtension3, IDocumentExtension4, CharSequence, IRegionComparible {
+
+	/**
+	 * This ThreadLocal construct is used so each thread can maintain its only
+	 * pointer to the double linked list that manages the documents regions.
+	 * The only thing we "gaurd" for is that a previously cached region has
+	 * been deleted.
+	 * 
+	 * The object that is kept in the thread local's map, is just a pointer to
+	 * an array position. That's because the object there needs to be "free"
+	 * from references to other objects, or it will not be garbage collected.
+	 */
+	private class CurrentDocumentRegionCache {
+		// I'm assuming for now there would never be so many threads that
+		// this arrayList needs to be bounded, or 'cleaned up'.
+		// this assumption should be tested in practice and long running
+		// jobs -- found not to be a good assumption. See below.
+		private List cachedRegionPositionArray = Collections.synchronizedList(new ArrayList());
+		private final boolean DEBUG = false;
+		private static final int MAX_SIZE = 50;
+
+
+		private ThreadLocal threadLocalCachePosition = new ThreadLocal();
+
+		IStructuredDocumentRegion get() {
+			IStructuredDocumentRegion region = null;
+			int pos = getThreadLocalPosition();
+			try {
+				region = (IStructuredDocumentRegion) cachedRegionPositionArray.get(pos);
+			}
+			catch (IndexOutOfBoundsException e) {
+				// even though the cachedRegionPosition is synchronized,
+				// that just means each access is syncronized, its
+				// still possible for another thread to cause it to
+				// be cleared, after this thread gets it position.
+				// So, if that happens, all we can do is reset to beginning.
+				// This should be extremely rare (in other words, probably
+				// not worth using synchronized blocks
+				// to access cachedRegionPositionArray.
+				reinitThreadLocalPosition();
+				resetToInitialState();
+			}
+			if (region == null) {
+				region = resetToInitialState();
+			}
+			else
+			// region not null
+			if (region.isDeleted()) {
+				region = resetToInitialState();
+			}
+			return region;
+		}
+
+		private int getThreadLocalPosition() {
+			Object threadLocalObject = threadLocalCachePosition.get();
+			int pos = -1;
+			if (threadLocalObject == null) {
+
+				pos = reinitThreadLocalPosition();
+			}
+			else {
+				pos = ((Integer) threadLocalObject).intValue();
+			}
+			return pos;
+		}
+
+		/**
+		 * @return
+		 */
+		private int reinitThreadLocalPosition() {
+			Integer position;
+			int pos;
+			// TODO_future: think of a better solution that doesn't
+			// require this kludge. This is especially required because
+			// some infrasture, such as reconciler, actually null out
+			// their thread object and recreate it, 500 msecs later
+			// (approximately).
+			// Note: the likely solution in future is to clear after every
+			// heavy use of getCachedRegion, such as in creating node
+			// lists, or reparsing or partioning.
+			if (cachedRegionPositionArray.size() > MAX_SIZE) {
+				cachedRegionPositionArray.clear();
+				if (DEBUG) {
+					System.out.println("cachedRegionPositionArray cleared at size " + MAX_SIZE); //$NON-NLS-1$
+				}
+			}
+			position = new Integer(cachedRegionPositionArray.size());
+			threadLocalCachePosition.set(position);
+			cachedRegionPositionArray.add(position.intValue(), null);
+			pos = position.intValue();
+			return pos;
+		}
+
+		private IStructuredDocumentRegion resetToInitialState() {
+			IStructuredDocumentRegion region;
+			region = getFirstStructuredDocumentRegion();
+			set(region);
+			return region;
+		}
+
+		void set(IStructuredDocumentRegion region) {
+			try {
+				int pos = getThreadLocalPosition();
+				cachedRegionPositionArray.set(pos, region);
+			}
+			catch (IndexOutOfBoundsException e) {
+				// even though the cachedRegionPosition is synchronized,
+				// that just means each access is syncronized, its
+				// still possible for another thread to cause it to
+				// be cleared, after this thread gets it position.
+				// So, if that happens, all we can do is reset to beginning.
+				// This should be extremely rare (in other words, probably
+				// not worth using synchronized blocks
+				// to access cachedRegionPositionArray.
+				reinitThreadLocalPosition();
+				resetToInitialState();
+			}
+		}
+	}
+
+	/**
+	 * This NullDocumentEvent is used to complete the "aboutToChange" and
+	 * "changed" cycle, when in fact the original change is no longer valid.
+	 * The only known (valid) case of this is when a model re-initialize takes
+	 * place, which causes setText to be called in the middle of some previous
+	 * change. [This architecture will be improved in future].
+	 */
+	public class NullDocumentEvent extends DocumentEvent {
+		public NullDocumentEvent() {
+			this(BasicStructuredDocument.this, 0, 0, ""); //$NON-NLS-1$
+		}
+
+		private NullDocumentEvent(IDocument doc, int offset, int length, String text) {
+			super(doc, offset, length, text);
+		}
+	}
+
+	static class RegisteredReplace {
+		/** The owner of this replace operation. */
+		IDocumentListener fOwner;
+		/** The replace operation */
+		IDocumentExtension.IReplace fReplace;
+
+		/**
+		 * Creates a new bundle object.
+		 * 
+		 * @param owner
+		 *            the document listener owning the replace operation
+		 * @param replace
+		 *            the replace operation
+		 */
+		RegisteredReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+			fOwner = owner;
+			fReplace = replace;
+		}
+	}
+
+	/**
+	 * these control variable isn't mark as 'final' since there's some unit
+	 * tests that manipulate it. For final product, it should be.
+	 */
+
+	private static boolean USE_LOCAL_THREAD = true;
+
+	/**
+	 * purely for debugging/performance measurements In practice, would always
+	 * be 'true'. (and should never be called by called by clients). Its not
+	 * 'final' or private just so it can be varied during
+	 * debugging/performance measurement runs.
+	 * 
+	 * @param use_local_thread
+	 */
+	public static void setUSE_LOCAL_THREAD(final boolean use_local_thread) {
+		USE_LOCAL_THREAD = use_local_thread;
+	}
+
+	private IStructuredDocumentRegion cachedDocumentRegion;
+	private EncodingMemento encodingMemento;
+	private boolean fAcceptPostNotificationReplaces = true;
+	private CurrentDocumentRegionCache fCurrentDocumentRegionCache;
+	private DocumentEvent fDocumentEvent;
+	private IDocumentListener[] fDocumentListeners;
+
+	/**
+	 * The registered document partitioners.
+	 */
+	private Map fDocumentPartitioners;
+	/** The registered document partitioning listeners */
+	private List fDocumentPartitioningListeners;
+	private IStructuredDocumentRegion firstDocumentRegion;
+	private RegionParser fParser;
+	private GenericPositionManager fPositionManager;
+	private List fPostNotificationChanges;
+	private IDocumentListener[] fPrenotifiedDocumentListeners;
+	private int fReentranceCount = 0;
+	private IStructuredTextReParser fReParser;
+	private int fStoppedCount = 0;
+
+	private ITextStore fStore;
+	private Object[] fStructuredDocumentAboutToChangeListeners;
+	private Object[] fStructuredDocumentChangedListeners;
+	private Object[] fStructuredDocumentChangingListeners;
+
+	private List fDocumentRewriteSessionListeners;
+
+	private ILineTracker fTracker;
+	private IStructuredTextUndoManager fUndoManager;
+	private IStructuredDocumentRegion lastDocumentRegion;
+
+	private byte[] listenerLock = new byte[0];
+	private NullDocumentEvent NULL_DOCUMENT_EVENT;
+
+	/**
+	 * Theoretically, a document can contain mixed line delimiters, but the
+	 * user's preference is usually to be internally consistent.
+	 */
+	private String fInitialLineDelimiter;
+	private static final String READ_ONLY_REGIONS_CATEGORY = "_READ_ONLY_REGIONS_CATEGORY_"; //$NON-NLS-1$
+	/**
+	 * Current rewrite session, or none if not presently rewriting.
+	 */
+	private DocumentRewriteSession fActiveRewriteSession;
+	/**
+	 * Last modification stamp, automatically updated on change.
+	 */
+	private long fModificationStamp;
+	/**
+	 * Keeps track of next modification stamp.
+	 */
+	private long fNextModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+	/**
+	 * debug variable only
+	 * 
+	 * @param parser
+	 */
+	private long startStreamTime;
+	/**
+	 * debug variable only
+	 * 
+	 * @param parser
+	 */
+	private long startTime;
+
+	public BasicStructuredDocument() {
+		super();
+		fCurrentDocumentRegionCache = new CurrentDocumentRegionCache();
+		setTextStore(new StructuredDocumentTextStore(50, 300));
+		setLineTracker(new DefaultLineTracker());
+		NULL_DOCUMENT_EVENT = new NullDocumentEvent();
+
+		internal_addPositionCategory(READ_ONLY_REGIONS_CATEGORY);
+		internal_addPositionUpdater(new DeleteEqualPositionUpdater(READ_ONLY_REGIONS_CATEGORY));
+
+	}
+
+	/**
+	 * This is the primary way to get a new structuredDocument. Its best to
+	 * use the factory methods in ModelManger to create a new
+	 * IStructuredDocument, since it will get and initialize the parser
+	 * according to the desired content type.
+	 */
+	public BasicStructuredDocument(RegionParser parser) {
+		this();
+		Assert.isNotNull(parser, "Program Error: IStructuredDocument can not be created with null parser"); //$NON-NLS-1$
+		// go through setter in case there is side effects
+		internal_setParser(parser);
+	}
+
+	private void _clearDocumentEvent() {
+		// no hard and fast requirement to null out ... just seems like
+		// a good idea, since we are done with it.
+		fDocumentEvent = null;
+	}
+
+	private void _fireDocumentAboutToChange(Object[] listeners) {
+		// most DocumentAboutToBeChanged listeners do not anticipate
+		// DocumentEvent == null. So make sure documentEvent is not
+		// null. (this should never happen, yet it does sometimes)
+		if (fDocumentEvent == null) {
+			fDocumentEvent = new NullDocumentEvent();
+		}
+		// we must assign listeners to local variable, since the add and
+		// remove
+		// listner
+		// methods can change the actual instance of the listener array from
+		// another thread
+		if (listeners != null) {
+			Object[] holdListeners = listeners;
+			// Note: the docEvent is created in replaceText API
+			// fire
+			for (int i = 0; i < holdListeners.length; i++) {
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					startTime = System.currentTimeMillis();
+				}
+				// safeguard from listeners that throw exceptions
+				try {
+					// this is a safe cast, since addListners requires a
+					// IStructuredDocumentListener
+					((IDocumentListener) holdListeners[i]).documentAboutToBeChanged(fDocumentEvent);
+				}
+				catch (Exception exception) {
+					Logger.logException(exception);
+				}
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					long stopTime = System.currentTimeMillis();
+					System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$
+				}
+			}
+		}
+	}
+
+	private void notifyDocumentPartitionersAboutToChange(DocumentEvent documentEvent) {
+		if (fDocumentPartitioners != null) {
+			Iterator e = fDocumentPartitioners.values().iterator();
+			while (e.hasNext()) {
+				IDocumentPartitioner p = (IDocumentPartitioner) e.next();
+				// safeguard from listeners that throw exceptions
+				try {
+					p.documentAboutToBeChanged(documentEvent);
+				}
+				catch (Exception exception) {
+					Logger.logException(exception);
+				}
+			}
+		}
+	}
+
+	private void _fireDocumentChanged(Object[] listeners, StructuredDocumentEvent event) {
+
+		// we must assign listeners to local variable, since the add and
+		// remove
+		// listner
+		// methods can change the actual instance of the listener array from
+		// another thread
+		if (listeners != null) {
+			Object[] holdListeners = listeners;
+			// NOTE: document event is created in replace Text API and setText
+			// API
+			// now fire
+			for (int i = 0; i < holdListeners.length; i++) {
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					startTime = System.currentTimeMillis();
+				}
+
+				// safeguard from listeners that throw exceptions
+				try {
+					// this is a safe cast, since addListners requires a
+					// IStructuredDocumentListener
+					// Notes: fDocumentEvent can be "suddenly" null, if one of
+					// the
+					// previous changes
+					// caused a "setText" to be called. The only known case of
+					// this
+					// is a model reset
+					// due to page directive changing. Eventually we should
+					// change
+					// archetecture to have
+					// event que and be able to "cancel" pending events, but
+					// for
+					// now, we'll just pass a
+					// NullDocumentEvent. By the way, it is important to send
+					// something, since clients might
+					// have indeterminant state due to "aboutToChange" being
+					// sent
+					// earlier.
+					if (fDocumentEvent == null) {
+						((IDocumentListener) holdListeners[i]).documentChanged(NULL_DOCUMENT_EVENT);
+					}
+					else {
+						((IDocumentListener) holdListeners[i]).documentChanged(fDocumentEvent);
+					}
+				}
+				catch (Exception exception) {
+					Logger.logException(exception);
+				}
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					long stopTime = System.currentTimeMillis();
+					System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$
+				}
+			}
+		}
+	}
+
+	private void notifyDocumentPartitionersDocumentChanged(DocumentEvent documentEvent) {
+		if (fDocumentPartitioners != null) {
+			Iterator e = fDocumentPartitioners.values().iterator();
+			while (e.hasNext()) {
+				IDocumentPartitioner p = (IDocumentPartitioner) e.next();
+				// safeguard from listeners that throw exceptions
+				try {
+					if (p instanceof IDocumentPartitionerExtension) {
+						// IRegion changedPartion =
+						((IDocumentPartitionerExtension) p).documentChanged2(documentEvent);
+					}
+					else {
+						p.documentChanged(documentEvent);
+					}
+				}
+				catch (Exception exception) {
+					Logger.logException(exception);
+				}
+			}
+		}
+	}
+
+
+	private void _fireEvent(Object[] listeners, NoChangeEvent event) {
+		// we must assign listeners to local variable, since the add and
+		// remove
+		// listner
+		// methods can change the actual instance of the listener array from
+		// another thread
+		if (listeners != null) {
+			Object[] holdListeners = listeners;
+			for (int i = 0; i < holdListeners.length; i++) {
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					startTime = System.currentTimeMillis();
+				}
+				// safeguard from listeners that throw exceptions
+				try {
+					// this is a safe cast, since addListners requires a
+					// IStructuredDocumentListener
+					((IStructuredDocumentListener) holdListeners[i]).noChange(event);
+				}
+				catch (Exception exception) {
+					Logger.logException(exception);
+				}
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					long stopTime = System.currentTimeMillis();
+					System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$
+				}
+			}
+		}
+	}
+
+	private void _fireEvent(Object[] listeners, RegionChangedEvent event) {
+		// we must assign listeners to local variable, since the add and
+		// remove
+		// listner
+		// methods can change the actual instance of the listener array from
+		// another thread
+		if (listeners != null) {
+			Object[] holdListeners = listeners;
+			for (int i = 0; i < holdListeners.length; i++) {
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					startTime = System.currentTimeMillis();
+				}
+				// safeguard from listeners that throw exceptions
+				try {
+					// this is a safe cast, since addListners requires a
+					// IStructuredDocumentListener
+					((IStructuredDocumentListener) holdListeners[i]).regionChanged(event);
+				}
+				catch (Exception exception) {
+					Logger.logException(exception);
+				}
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					long stopTime = System.currentTimeMillis();
+					System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$
+				}
+			}
+		}
+	}
+
+	private void _fireEvent(Object[] listeners, RegionsReplacedEvent event) {
+		// we must assign listeners to local variable, since the add and
+		// remove
+		// listner
+		// methods can change the actual instance of the listener array from
+		// another thread
+		if (listeners != null) {
+			Object[] holdListeners = listeners;
+			for (int i = 0; i < holdListeners.length; i++) {
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					startTime = System.currentTimeMillis();
+				}
+				// safeguard from listeners that throw exceptions
+				try {
+					// this is a safe cast, since addListners requires a
+					// IStructuredDocumentListener
+					((IStructuredDocumentListener) holdListeners[i]).regionsReplaced(event);
+				}
+				catch (Exception exception) {
+					Logger.logException(exception);
+				}
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					long stopTime = System.currentTimeMillis();
+					System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$
+				}
+			}
+		}
+	}
+
+	private void _fireEvent(Object[] listeners, StructuredDocumentRegionsReplacedEvent event) {
+		// we must assign listeners to local variable, since the add and
+		// remove
+		// listner
+		// methods can change the actual instance of the listener array from
+		// another thread
+		if (listeners != null) {
+			Object[] holdListeners = listeners;
+			for (int i = 0; i < holdListeners.length; i++) {
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					startTime = System.currentTimeMillis();
+				}
+				// safeguard from listeners that throw exceptions
+				try {
+					// this is a safe cast, since addListners requires a
+					// IStructuredDocumentListener
+					((IStructuredDocumentListener) holdListeners[i]).nodesReplaced(event);
+				}
+				catch (Exception exception) {
+					Logger.logException(exception);
+				}
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					long stopTime = System.currentTimeMillis();
+					System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$
+				}
+			}
+		}
+	}
+
+	private void _fireStructuredDocumentAboutToChange(Object[] listeners) {
+		// we must assign listeners to local variable, since the add and
+		// remove
+		// listner
+		// methods can change the actual instance of the listener array from
+		// another thread
+		if (listeners != null) {
+			Object[] holdListeners = listeners;
+			// Note: the docEvent is created in replaceText API
+			// fire
+			for (int i = 0; i < holdListeners.length; i++) {
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					startTime = System.currentTimeMillis();
+				}
+				// safeguard from listeners that throw exceptions
+				try {
+					// notice the AboutToBeChangedEvent is created from the
+					// DocumentEvent, since it is (nearly)
+					// the same information. ?What to do about
+					// originalRequester?
+					if (fDocumentEvent == null) {
+						fDocumentEvent = new NullDocumentEvent();
+					}
+					AboutToBeChangedEvent aboutToBeChangedEvent = new AboutToBeChangedEvent(this, null, fDocumentEvent.getText(), fDocumentEvent.getOffset(), fDocumentEvent.getLength());
+					// this is a safe cast, since addListners requires a
+					// IStructuredDocumentListener
+					((IModelAboutToBeChangedListener) holdListeners[i]).modelAboutToBeChanged(aboutToBeChangedEvent);
+				}
+				catch (Exception exception) {
+					Logger.logException(exception);
+				}
+				if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) {
+					long stopTime = System.currentTimeMillis();
+					System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$
+				}
+			}
+		}
+	}
+
+	protected void acquireLock() {
+		// do nothing here in super class
+	}
+
+	/**
+	 * addModelAboutToBeChangedListener method comment.
+	 */
+	public void addDocumentAboutToChangeListener(IModelAboutToBeChangedListener listener) {
+		synchronized (listenerLock) {
+
+			// make sure listener is not already in listening
+			// (and if it is, print a warning to aid debugging, if needed)
+			if (!Utilities.contains(fStructuredDocumentAboutToChangeListeners, listener)) {
+				int oldSize = 0;
+				if (fStructuredDocumentAboutToChangeListeners != null) {
+					// normally won't be null, but we need to be sure, for
+					// first
+					// time through
+					oldSize = fStructuredDocumentAboutToChangeListeners.length;
+				}
+				int newSize = oldSize + 1;
+				Object[] newListeners = new Object[newSize];
+				if (fStructuredDocumentAboutToChangeListeners != null) {
+					System.arraycopy(fStructuredDocumentAboutToChangeListeners, 0, newListeners, 0, oldSize);
+				}
+				// add listener to last position
+				newListeners[newSize - 1] = listener;
+				//
+				// now switch new for old
+				fStructuredDocumentAboutToChangeListeners = newListeners;
+				//
+			}
+		}
+	}
+
+	/**
+	 * The StructuredDocumentListners and ModelChagnedListeners are very
+	 * similar. They both receive identical events. The difference is the
+	 * timing. The "pure" StructuredDocumentListners are notified after the
+	 * structuredDocument has been changed, but before other, related models
+	 * may have been changed such as the Structural Model. The Structural
+	 * model is in fact itself a "pure" StructuredDocumentListner. The
+	 * ModelChangedListeners can rest assured that all models and data have
+	 * been updated from the change by the tiem they are notified. This is
+	 * especially important for the text widget, for example, which may rely
+	 * on both structuredDocument and structural model information.
+	 */
+	public void addDocumentChangedListener(IStructuredDocumentListener listener) {
+		synchronized (listenerLock) {
+
+			if (Debug.debugStructuredDocument) {
+				System.out.println("IStructuredDocument::addModelChangedListener. Request to add an instance of " + listener.getClass() + " as a listener on structuredDocument."); //$NON-NLS-2$//$NON-NLS-1$
+			}
+			// make sure listener is not already in listening
+			// (and if it is, print a warning to aid debugging, if needed)
+			if (Utilities.contains(fStructuredDocumentChangedListeners, listener)) {
+				if (Debug.displayWarnings) {
+					System.out.println("IStructuredDocument::addModelChangedListener. listener " + listener + " was addeded more than once. "); //$NON-NLS-2$//$NON-NLS-1$
+				}
+			}
+			else {
+				if (Debug.debugStructuredDocument) {
+					System.out.println("IStructuredDocument::addModelChangedListener. Adding an instance of " + listener.getClass() + " as a listener on structuredDocument."); //$NON-NLS-2$//$NON-NLS-1$
+				}
+				int oldSize = 0;
+				if (fStructuredDocumentChangedListeners != null) {
+					// normally won't be null, but we need to be sure, for
+					// first
+					// time through
+					oldSize = fStructuredDocumentChangedListeners.length;
+				}
+				int newSize = oldSize + 1;
+				Object[] newListeners = new Object[newSize];
+				if (fStructuredDocumentChangedListeners != null) {
+					System.arraycopy(fStructuredDocumentChangedListeners, 0, newListeners, 0, oldSize);
+				}
+				// add listener to last position
+				newListeners[newSize - 1] = listener;
+				//
+				// now switch new for old
+				fStructuredDocumentChangedListeners = newListeners;
+				//
+				// when a listener is added,
+				// send the new model event to that one particular listener,
+				// so it
+				// can initialize itself with the current state of the model
+				// listener.newModel(new NewModelEvent(this, listener));
+			}
+		}
+	}
+
+	public void addDocumentChangingListener(IStructuredDocumentListener listener) {
+		synchronized (listenerLock) {
+
+			if (Debug.debugStructuredDocument) {
+				System.out.println("IStructuredDocument::addStructuredDocumentListener. Request to add an instance of " + listener.getClass() + " as a listener on structuredDocument."); //$NON-NLS-2$//$NON-NLS-1$
+			}
+			// make sure listener is not already in listening
+			// (and if it is, print a warning to aid debugging, if needed)
+			if (Utilities.contains(fStructuredDocumentChangingListeners, listener)) {
+				if (Debug.displayWarnings) {
+					System.out.println("IStructuredDocument::addStructuredDocumentListener. listener " + listener + " was addeded more than once. "); //$NON-NLS-2$//$NON-NLS-1$
+				}
+			}
+			else {
+				if (Debug.debugStructuredDocument) {
+					System.out.println("IStructuredDocument::addStructuredDocumentListener. Adding an instance of " + listener.getClass() + " as a listener on structuredDocument."); //$NON-NLS-2$//$NON-NLS-1$
+				}
+				int oldSize = 0;
+				if (fStructuredDocumentChangingListeners != null) {
+					// normally won't be null, but we need to be sure, for
+					// first
+					// time through
+					oldSize = fStructuredDocumentChangingListeners.length;
+				}
+				int newSize = oldSize + 1;
+				Object[] newListeners = new Object[newSize];
+				if (fStructuredDocumentChangingListeners != null) {
+					System.arraycopy(fStructuredDocumentChangingListeners, 0, newListeners, 0, oldSize);
+				}
+				// add listener to last position
+				newListeners[newSize - 1] = listener;
+				//
+				// now switch new for old
+				fStructuredDocumentChangingListeners = newListeners;
+				//
+				// when a listener is added,
+				// send the new model event to that one particular listener,
+				// so it
+				// can initialize itself with the current state of the model
+				// listener.newModel(new NewModelEvent(this, listener));
+			}
+		}
+	}
+
+	/**
+	 * We manage our own document listners, instead of delegating to our
+	 * parentDocument, so we can fire at very end (and not when the
+	 * parentDocument changes).
+	 * 
+	 */
+	public void addDocumentListener(IDocumentListener listener) {
+		synchronized (listenerLock) {
+
+			// make sure listener is not already in listening
+			// (and if it is, print a warning to aid debugging, if needed)
+			if (!Utilities.contains(fDocumentListeners, listener)) {
+				int oldSize = 0;
+				if (fDocumentListeners != null) {
+					// normally won't be null, but we need to be sure, for
+					// first
+					// time through
+					oldSize = fDocumentListeners.length;
+				}
+				int newSize = oldSize + 1;
+				IDocumentListener[] newListeners = null;
+				newListeners = new IDocumentListener[newSize];
+				if (fDocumentListeners != null) {
+					System.arraycopy(fDocumentListeners, 0, newListeners, 0, oldSize);
+				}
+				// add listener to last position
+				newListeners[newSize - 1] = listener;
+				// now switch new for old
+				fDocumentListeners = newListeners;
+			}
+		}
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.IDocument#addDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
+	 * 
+	 * Registers the document partitioning listener with the document. After
+	 * registration the IDocumentPartitioningListener is informed about each
+	 * partition change cause by a document manipulation. If a document
+	 * partitioning listener is also a document listener, the following
+	 * notification sequence is guaranteed if a document manipulation changes
+	 * the document partitioning: 1)
+	 * listener.documentAboutToBeChanged(DocumentEvent); 2)
+	 * listener.documentPartitioningChanged(); 3)
+	 * listener.documentChanged(DocumentEvent); If the listener is already
+	 * registered nothing happens.
+	 * 
+	 * @see IDocumentPartitioningListener
+	 */
+
+	public void addDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+		synchronized (listenerLock) {
+
+			Assert.isNotNull(listener);
+			if (fDocumentPartitioningListeners == null) {
+				fDocumentPartitioningListeners = new ArrayList(1);
+			}
+			if (!fDocumentPartitioningListeners.contains(listener))
+				fDocumentPartitioningListeners.add(listener);
+		}
+	}
+
+	/**
+	 * Adds the position to the document's default position category. The
+	 * default category must be specified by the implementer. A position that
+	 * has been added to a position category is updated at each change applied
+	 * to the document.
+	 * 
+	 * @exception BadLocationException
+	 *                If position is not a valid range in the document
+	 */
+	public void addPosition(Position position) throws BadLocationException {
+		getPositionManager().addPosition(position);
+	}
+
+	/**
+	 * @see IDocument#addPosition
+	 * @exception BadLocationException
+	 *                If position is not a valid range in the document
+	 * @exception BadPositionCategoryException
+	 *                If the category is not defined for the document
+	 */
+	public void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException {
+		getPositionManager().addPosition(category, position);
+	}
+
+	/**
+	 * @see IDocument#addPositionCategory
+	 */
+	public void addPositionCategory(String category) {
+		internal_addPositionCategory(category);
+	}
+
+	/**
+	 * @see IDocument#addPositionUpdater
+	 */
+	public void addPositionUpdater(IPositionUpdater updater) {
+		internal_addPositionUpdater(updater);
+	}
+
+	/**
+	 * Adds the given document listener as one which is notified before those
+	 * document listeners added with <code>addDocumentListener</code> are
+	 * notified. If the given listener is also registered using
+	 * <code>addDocumentListener</code> it will be notified twice. If the
+	 * listener is already registered nothing happens.
+	 * <p>
+	 * 
+	 * This method is not for public use, it may only be called by
+	 * implementers of <code>IDocumentAdapter</code> and only if those
+	 * implementers need to implement <code>IDocumentListener</code>.
+	 * 
+	 * @param documentAdapter
+	 *            the listener to be added as prenotified document listener
+	 */
+	public void addPrenotifiedDocumentListener(IDocumentListener documentAdapter) {
+		synchronized (listenerLock) {
+
+			if (fPrenotifiedDocumentListeners != null) {
+				int previousSize = fPrenotifiedDocumentListeners.length;
+				IDocumentListener[] listeners = new IDocumentListener[previousSize + 1];
+				System.arraycopy(fPrenotifiedDocumentListeners, 0, listeners, 0, previousSize);
+				listeners[previousSize] = documentAdapter;
+				fPrenotifiedDocumentListeners = listeners;
+			}
+			else {
+				fPrenotifiedDocumentListeners = new IDocumentListener[1];
+				fPrenotifiedDocumentListeners[0] = documentAdapter;
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.CharSequence#charAt(int)
+	 */
+	public char charAt(int arg0) {
+		try {
+			return getChar(0);
+		}
+		catch (BadLocationException e) {
+			throw new IndexOutOfBoundsException();
+		}
+	}
+
+	/**
+	 * This form of the API removes all read only positions, as should be done
+	 * we 'setText' is called. Note: an alternative algorithm may simply
+	 * remove the category (and it would get added back in later, if/when
+	 * readonly regions added.
+	 */
+	private void clearReadOnly() {
+		Position[] positions = null;
+		try {
+			positions = getPositions(READ_ONLY_REGIONS_CATEGORY);
+		}
+		catch (BadPositionCategoryException e) {
+			Logger.logException("program error: should never occur", e); //$NON-NLS-1$
+		}
+		for (int i = 0; i < positions.length; i++) {
+			Position position = positions[i];
+			// note we don't fire the "about to change" or "changed" events,
+			// since presumably, text is all going away and being replaced
+			// anyway.
+			position.delete();
+		}
+	}
+
+
+	public void clearReadOnly(int startOffset, int length) {
+		// TODO DW I still need to implement smarter algorithm that
+		// adust existing RO regions, if needed. For now, I'll just
+		// remove any that overlap.
+		try {
+			Position[] positions = getPositions(READ_ONLY_REGIONS_CATEGORY);
+			for (int i = 0; i < positions.length; i++) {
+				Position position = positions[i];
+				if (position.overlapsWith(startOffset, length)) {
+					String effectedText = this.get(startOffset, length);
+					// fDocumentEvent = new DocumentEvent(this, startOffset,
+					// length, effectedText);
+					fireReadOnlyAboutToBeChanged();
+					position.delete();
+					NoChangeEvent noChangeEvent = new NoChangeEvent(this, null, effectedText, startOffset, length);
+					noChangeEvent.reason = NoChangeEvent.READ_ONLY_STATE_CHANGE;
+					fireReadOnlyStructuredDocumentEvent(noChangeEvent);
+				}
+			}
+		}
+		catch (BadPositionCategoryException e) {
+			// just means no readonly regions been defined yet
+			// so nothing to do.
+		}
+	}
+
+	/**
+	 * Computes the index at which a <code>Position</code> with the
+	 * specified offset would be inserted into the given category. As the
+	 * ordering inside a category only depends on the offset, the index must
+	 * be choosen to be the first of all positions with the same offset.
+	 * 
+	 * @param category
+	 *            the category in which would be added
+	 * @param offset
+	 *            the position offset to be considered
+	 * @return the index into the category
+	 * @exception BadLocationException
+	 *                if offset is invalid in this document
+	 * @exception BadPositionCategoryException
+	 *                if category is undefined in this document
+	 */
+	public int computeIndexInCategory(String category, int offset) throws org.eclipse.jface.text.BadPositionCategoryException, org.eclipse.jface.text.BadLocationException {
+		return getPositionManager().computeIndexInCategory(category, offset);
+	}
+
+	/**
+	 * Computes the number of lines in the given text. For a given implementer
+	 * of this interface this method returns the same result as
+	 * <code>set(text); getNumberOfLines()</code>.
+	 * 
+	 * @param text
+	 *            the text whose number of lines should be computed
+	 * @return the number of lines in the given text
+	 */
+	public int computeNumberOfLines(String text) {
+		return getTracker().computeNumberOfLines(text);
+	}
+
+	/**
+	 * Computes the partitioning of the given document range using the
+	 * document's partitioner.
+	 * 
+	 * @param offset
+	 *            the document offset at which the range starts
+	 * @param length
+	 *            the length of the document range
+	 * @return a specification of the range's partitioning
+	 * @throws BadLocationException
+	 * @throws BadPartitioningException
+	 */
+	public ITypedRegion[] computePartitioning(int offset, int length) throws BadLocationException {
+		ITypedRegion[] typedRegions = null;
+		try {
+			typedRegions = computePartitioning(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING, offset, length, false);
+		}
+		catch (BadPartitioningException e) {
+			// impossible in this context
+			throw new Error(e);
+		}
+		if (typedRegions == null) {
+			typedRegions = new ITypedRegion[0];
+		}
+		return typedRegions;
+	}
+
+
+	public ITypedRegion[] computePartitioning(String partitioning, int offset, int length, boolean includeZeroLengthPartitions) throws BadLocationException, BadPartitioningException {
+		if ((0 > offset) || (0 > length) || (offset + length > getLength()))
+			throw new BadLocationException();
+
+		IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning);
+
+		if (partitioner instanceof IDocumentPartitionerExtension2)
+			return ((IDocumentPartitionerExtension2) partitioner).computePartitioning(offset, length, includeZeroLengthPartitions);
+		else if (partitioner != null)
+			return partitioner.computePartitioning(offset, length);
+		else if (IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING.equals(partitioning))
+			return new TypedRegion[]{new TypedRegion(offset, length, DEFAULT_CONTENT_TYPE)};
+		else
+			throw new BadPartitioningException();
+	}
+
+	/**
+	 * @see IDocument#containsPosition
+	 */
+	public boolean containsPosition(String category, int offset, int length) {
+		return getPositionManager().containsPosition(category, offset, length);
+	}
+
+	/**
+	 * @see IDocument#containsPositionCategory
+	 */
+	public boolean containsPositionCategory(String category) {
+		return getPositionManager().containsPositionCategory(category);
+	}
+
+	public boolean containsReadOnly(int startOffset, int length) {
+		boolean result = false;
+		try {
+			Position[] positions = getPositions(READ_ONLY_REGIONS_CATEGORY);
+			for (int i = 0; i < positions.length; i++) {
+				Position position = positions[i];
+				if (position.overlapsWith(startOffset, length)) {
+					result = true;
+					break;
+				}
+			}
+		}
+		catch (BadPositionCategoryException e) {
+			// just means no readonly regions been defined yet
+			// so obviously false
+			result = false;
+		}
+		return result;
+	}
+
+	private void executePostNotificationChanges() {
+		if (fStoppedCount > 0)
+			return;
+		while (fPostNotificationChanges != null) {
+			List changes = fPostNotificationChanges;
+			fPostNotificationChanges = null;
+			Iterator e = changes.iterator();
+			while (e.hasNext()) {
+				RegisteredReplace replace = (RegisteredReplace) e.next();
+				replace.fReplace.perform(this, replace.fOwner);
+			}
+		}
+	}
+
+	private void fireDocumentAboutToChanged() {
+		// most DocumentAboutToBeChanged listeners do not anticipate
+		// DocumentEvent == null. So make sure documentEvent is not
+		// null. (this should never happen, yet it does sometimes)
+		if (fDocumentEvent == null) {
+			fDocumentEvent = new NullDocumentEvent();
+		}
+
+		_fireStructuredDocumentAboutToChange(fStructuredDocumentAboutToChangeListeners);
+		// Note: the docEvent is created in replaceText API! (or set Text)
+		_fireDocumentAboutToChange(fPrenotifiedDocumentListeners);
+		notifyDocumentPartitionersAboutToChange(fDocumentEvent);
+		_fireDocumentAboutToChange(fDocumentListeners);
+	}
+
+	/**
+	 * Fires the document partitioning changed notification to all registered
+	 * document partitioning listeners. Uses a robust iterator.
+	 * 
+	 * @param event
+	 *            the document partitioning changed event
+	 * 
+	 * @see IDocumentPartitioningListenerExtension2
+	 */
+	protected void fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent event) {
+		if (fDocumentPartitioningListeners == null || fDocumentPartitioningListeners.size() == 0)
+			return;
+
+		List list = new ArrayList(fDocumentPartitioningListeners);
+		Iterator e = list.iterator();
+		while (e.hasNext()) {
+			IDocumentPartitioningListener l = (IDocumentPartitioningListener) e.next();
+			if (l instanceof IDocumentPartitioningListenerExtension2) {
+				IDocumentPartitioningListenerExtension2 extension2 = (IDocumentPartitioningListenerExtension2) l;
+				extension2.documentPartitioningChanged(event);
+			}
+			else if (l instanceof IDocumentPartitioningListenerExtension) {
+				IDocumentPartitioningListenerExtension extension = (IDocumentPartitioningListenerExtension) l;
+				extension.documentPartitioningChanged(this, event.getCoverage());
+			}
+			else {
+				l.documentPartitioningChanged(this);
+			}
+		}
+
+	}
+
+	private void fireReadOnlyAboutToBeChanged() {
+		_fireStructuredDocumentAboutToChange(fStructuredDocumentAboutToChangeListeners);
+		// Note: the docEvent is created in replaceText API! (or set Text)
+		// _fireDocumentAboutToChange(fPrenotifiedDocumentListeners);
+		// _fireDocumentAboutToChange(fDocumentListeners);
+	}
+
+	private void fireReadOnlyStructuredDocumentEvent(NoChangeEvent event) {
+		_fireEvent(fStructuredDocumentChangingListeners, event);
+		_fireEvent(fStructuredDocumentChangedListeners, event);
+		// _fireDocumentChanged(fPrenotifiedDocumentListeners, event);
+		// _fireDocumentChanged(fDocumentListeners, event);
+		// _clearDocumentEvent();
+	}
+
+	private void fireStructuredDocumentEvent(NoChangeEvent event) {
+		_fireEvent(fStructuredDocumentChangingListeners, event);
+		_fireEvent(fStructuredDocumentChangedListeners, event);
+		_fireDocumentChanged(fPrenotifiedDocumentListeners, event);
+		notifyDocumentPartitionersDocumentChanged(event);
+		_fireDocumentChanged(fDocumentListeners, event);
+		_clearDocumentEvent();
+	}
+
+	private void fireStructuredDocumentEvent(RegionChangedEvent event) {
+		_fireEvent(fStructuredDocumentChangingListeners, event);
+		_fireEvent(fStructuredDocumentChangedListeners, event);
+		_fireDocumentChanged(fPrenotifiedDocumentListeners, event);
+		notifyDocumentPartitionersDocumentChanged(event);
+		_fireDocumentChanged(fDocumentListeners, event);
+		_clearDocumentEvent();
+	}
+
+	private void fireStructuredDocumentEvent(RegionsReplacedEvent event) {
+		_fireEvent(fStructuredDocumentChangingListeners, event);
+		_fireEvent(fStructuredDocumentChangedListeners, event);
+		_fireDocumentChanged(fPrenotifiedDocumentListeners, event);
+		notifyDocumentPartitionersDocumentChanged(event);
+		_fireDocumentChanged(fDocumentListeners, event);
+		_clearDocumentEvent();
+	}
+
+	private void fireStructuredDocumentEvent(StructuredDocumentRegionsReplacedEvent event) {
+		_fireEvent(fStructuredDocumentChangingListeners, event);
+		_fireEvent(fStructuredDocumentChangedListeners, event);
+		_fireDocumentChanged(fPrenotifiedDocumentListeners, event);
+		notifyDocumentPartitionersDocumentChanged(event);
+		_fireDocumentChanged(fDocumentListeners, event);
+		_clearDocumentEvent();
+	}
+
+	/**
+	 * Returns the document's complete text.
+	 */
+	public String get() {
+		return getStore().get(0, getLength());
+	}
+
+	/**
+	 * Returns length characters from the document's text starting from the
+	 * specified position.
+	 * 
+	 * @throws BadLocationException
+	 * 
+	 * @exception BadLocationException
+	 *                If the range is not valid in the document
+	 */
+	public String get(int offset, int length) {
+		String result = null;
+		int myLength = getLength();
+		if (0 > offset)
+			offset = 0;
+		if (0 > length)
+			length = 0;
+		if (offset + length > myLength) {
+			// first try adjusting length to fit
+			int lessLength = myLength - offset;
+			if ((lessLength >= 0) && (offset + lessLength == myLength)) {
+				length = lessLength;
+			}
+			else {
+				// second, try offset
+				int moreOffset = myLength - length;
+				if ((moreOffset >= 0) && (moreOffset + length == myLength)) {
+					offset = moreOffset;
+				}
+				else {
+					// can happen if myLength is 0.
+					// no adjustment possible.
+					result = new String();
+				}
+			}
+
+		}
+		if (result == null) {
+			result = getStore().get(offset, length);
+		}
+		return result;
+	}
+
+	public Object getAdapter(Class adapter) {
+		return Platform.getAdapterManager().getAdapter(this, adapter);
+	}
+
+	IStructuredDocumentRegion getCachedDocumentRegion() {
+		IStructuredDocumentRegion result = null;
+		if (USE_LOCAL_THREAD) {
+			result = fCurrentDocumentRegionCache.get();
+		}
+		else {
+			result = cachedDocumentRegion;
+		}
+		return result;
+	}
+
+	/**
+	 * @see IDocument#getChar
+	 * @exception BadLocationException
+	 *                If position is not a valid range in the document
+	 */
+	public char getChar(int pos) throws BadLocationException {
+		char result = 0x00;
+		try {
+			result = getStore().get(pos);
+		}
+		catch (IndexOutOfBoundsException e) {
+			throw new BadLocationException(e.getLocalizedMessage());
+		}
+		return result;
+	}
+
+	/**
+	 * Returns the type of the document partition containing the given
+	 * character position.
+	 */
+	public String getContentType(int offset) throws BadLocationException {
+		return getDocumentPartitioner().getContentType(offset);
+	}
+
+
+	public String getContentType(String partitioning, int offset, boolean preferOpenPartitions) throws BadLocationException, BadPartitioningException {
+		if ((0 > offset) || (offset > getLength()))
+			throw new BadLocationException();
+
+		IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning);
+
+		if (partitioner instanceof IDocumentPartitionerExtension2)
+			return ((IDocumentPartitionerExtension2) partitioner).getContentType(offset, preferOpenPartitions);
+		else if (partitioner != null)
+			return partitioner.getContentType(offset);
+		else if (IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING.equals(partitioning))
+			return DEFAULT_CONTENT_TYPE;
+		else
+			throw new BadPartitioningException();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension4#getDefaultLineDelimiter()
+	 */
+	public String getDefaultLineDelimiter() {
+		
+		String lineDelimiter= null;
+		
+		try {
+			lineDelimiter= getLineDelimiter(0);
+		} catch (BadLocationException x) {
+		}
+	
+		if (lineDelimiter != null)
+			return lineDelimiter;
+
+		if (fInitialLineDelimiter != null)
+			return fInitialLineDelimiter;
+
+		String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$
+		String[] delimiters= getLegalLineDelimiters();
+		Assert.isTrue(delimiters.length > 0);
+		for (int i= 0; i < delimiters.length; i++) {
+			if (delimiters[i].equals(sysLineDelimiter)) {
+				lineDelimiter= sysLineDelimiter;
+				break;
+			}
+		}
+		
+		if (lineDelimiter == null)
+			lineDelimiter= delimiters[0];
+	
+		return lineDelimiter;
+		
+	}
+
+	/**
+	 * Returns the document's partitioner.
+	 * 
+	 * @see IDocumentPartitioner
+	 */
+	public IDocumentPartitioner getDocumentPartitioner() {
+		return getDocumentPartitioner(IDocumentExtension3.DEFAULT_PARTITIONING);
+	}
+
+
+	public IDocumentPartitioner getDocumentPartitioner(String partitioning) {
+
+		IDocumentPartitioner documentPartitioner = null;
+		if (fDocumentPartitioners != null) {
+			documentPartitioner = (IDocumentPartitioner) fDocumentPartitioners.get(partitioning);
+		}
+		return documentPartitioner;
+	}
+
+	public EncodingMemento getEncodingMemento() {
+		return encodingMemento;
+	}
+
+	public IStructuredDocumentRegion getFirstStructuredDocumentRegion() {
+		// should we update cachedNode?
+		// We should to keep consistent philosophy of remembering last
+		// requested position,
+		// for efficiency.
+		setCachedDocumentRegion(firstDocumentRegion);
+		return firstDocumentRegion;
+	}
+
+	public IStructuredDocumentRegion getLastStructuredDocumentRegion() {
+		// should we update cachedNode?
+		// We should to keep consistent philosophy of remembering last
+		// requested position,
+		// for efficiency.
+		setCachedDocumentRegion(lastDocumentRegion);
+		return lastDocumentRegion;
+	}
+
+	/*
+	 * -------------------------- partitions
+	 * ----------------------------------
+	 */
+	public String[] getLegalContentTypes() {
+		String[] result = null;
+		try {
+			result = getLegalContentTypes(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING);
+		}
+		catch (BadPartitioningException e) {
+			// impossible in this context
+			throw new Error(e);
+		}
+		return result;
+	}
+
+	public String[] getLegalContentTypes(String partitioning) throws BadPartitioningException {
+		IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning);
+		if (partitioner != null)
+			return partitioner.getLegalContentTypes();
+		if (IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING.equals(partitioning))
+			return new String[]{DEFAULT_CONTENT_TYPE};
+		throw new BadPartitioningException();
+	}
+
+	/*
+	 * ------------------ line delimiter conversion
+	 * ---------------------------
+	 */
+	public String[] getLegalLineDelimiters() {
+		return getTracker().getLegalLineDelimiters();
+	}
+
+	/**
+	 * @see IDocument#getLength
+	 */
+	public int getLength() {
+		return getStore().getLength();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument#getLineDelimiter()
+	 */
+	public String getLineDelimiter() {
+		return getDefaultLineDelimiter();
+	}
+
+	/**
+	 * Returns the line delimiter of that line
+	 * 
+	 * @exception BadLocationException
+	 *                If the line number is invalid in the document
+	 */
+	public String getLineDelimiter(int line) throws org.eclipse.jface.text.BadLocationException {
+		return getTracker().getLineDelimiter(line);
+	}
+
+	/**
+	 * Returns a description of the specified line. The line is described by
+	 * its offset and its length excluding the line's delimiter.
+	 * 
+	 * @param line
+	 *            the line of interest
+	 * @return a line description
+	 * @exception BadLocationException
+	 *                if the line number is invalid in this document
+	 */
+	public org.eclipse.jface.text.IRegion getLineInformation(int line) throws org.eclipse.jface.text.BadLocationException {
+		return getTracker().getLineInformation(line);
+	}
+
+	/**
+	 * Returns a description of the line at the given offset. The description
+	 * contains the offset and the length of the line excluding the line's
+	 * delimiter.
+	 * 
+	 * @param offset
+	 *            the offset whose line should be described
+	 * @return a region describing the line
+	 * @exception BadLocationException
+	 *                if offset is invalid in this document
+	 */
+	public org.eclipse.jface.text.IRegion getLineInformationOfOffset(int offset) throws org.eclipse.jface.text.BadLocationException {
+		return getTracker().getLineInformationOfOffset(offset);
+	}
+
+	/*
+	 * ---------------------- line information
+	 * --------------------------------
+	 */
+	public int getLineLength(int line) throws org.eclipse.jface.text.BadLocationException {
+		return getTracker().getLineLength(line);
+	}
+
+	/**
+	 * Determines the offset of the first character of the given line.
+	 * 
+	 * @param line
+	 *            the line of interest
+	 * @return the document offset
+	 * @exception BadLocationException
+	 *                if the line number is invalid in this document
+	 */
+	public int getLineOffset(int line) throws org.eclipse.jface.text.BadLocationException {
+		return getTracker().getLineOffset(line);
+	}
+
+	public int getLineOfOffset(int offset) {
+		int result = -1;
+		try {
+			result = getTracker().getLineNumberOfOffset(offset);
+		}
+		catch (BadLocationException e) {
+			if (Logger.DEBUG_DOCUMENT)
+				Logger.log(Logger.INFO, "Dev. Program Info Only: IStructuredDocument::getLineOfOffset: offset out of range, zero assumed. offset = " + offset, e); //$NON-NLS-1$ //$NON-NLS-2$
+			result = 0;
+		}
+		return result;
+	}
+
+	/**
+	 * Returns the number of lines in this document
+	 * 
+	 * @return the number of lines in this document
+	 */
+	public int getNumberOfLines() {
+		return getTracker().getNumberOfLines();
+	}
+
+	/**
+	 * Returns the number of lines which are occupied by a given text range.
+	 * 
+	 * @param offset
+	 *            the offset of the specified text range
+	 * @param length
+	 *            the length of the specified text range
+	 * @return the number of lines occupied by the specified range
+	 * @exception BadLocationException
+	 *                if specified range is invalid in this tracker
+	 */
+	public int getNumberOfLines(int offset, int length) throws org.eclipse.jface.text.BadLocationException {
+		return getTracker().getNumberOfLines(offset, length);
+	}
+
+	/**
+	 * This is public, temporarily, for use by tag lib classes.
+	 */
+	public RegionParser getParser() {
+		if (fParser == null) {
+			throw new IllegalStateException("IStructuredDocument::getParser. Parser needs to be set before use"); //$NON-NLS-1$
+		}
+		return fParser;
+	}
+
+	/**
+	 * Returns the document partition in which the position is located. The
+	 * partition is specified as typed region.
+	 */
+	public ITypedRegion getPartition(int offset) throws BadLocationException {
+		ITypedRegion partition = null;
+		try {
+			partition = getPartition(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING, offset, false);
+		}
+		catch (BadPartitioningException e) {
+			throw new Error(e);
+		}
+		if (partition == null) {
+			throw new Error();
+		}
+		return partition;
+	}
+
+
+	public ITypedRegion getPartition(String partitioning, int offset, boolean preferOpenPartitions) throws BadLocationException, BadPartitioningException {
+		if ((0 > offset) || (offset > getLength()))
+			throw new BadLocationException();
+		ITypedRegion result = null;
+
+		IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning);
+
+		if (partitioner instanceof IDocumentPartitionerExtension2) {
+			result = ((IDocumentPartitionerExtension2) partitioner).getPartition(offset, preferOpenPartitions);
+		}
+		else if (partitioner != null) {
+			result = partitioner.getPartition(offset);
+		}
+		else if (IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING.equals(partitioning)) {
+			result = new TypedRegion(0, getLength(), DEFAULT_CONTENT_TYPE);
+		}
+		else
+			throw new BadPartitioningException();
+		return result;
+	}
+
+
+	public String[] getPartitionings() {
+		if (fDocumentPartitioners == null)
+			return new String[0];
+		String[] partitionings = new String[fDocumentPartitioners.size()];
+		fDocumentPartitioners.keySet().toArray(partitionings);
+		return partitionings;
+	}
+
+	/**
+	 * Returns all position categories added to this document.
+	 */
+	public String[] getPositionCategories() {
+		return getPositionManager().getPositionCategories();
+	}
+
+	/**
+	 * @return Returns the positionManager.
+	 */
+	private GenericPositionManager getPositionManager() {
+		if (fPositionManager == null) {
+			fPositionManager = new GenericPositionManager(this);
+		}
+		return fPositionManager;
+	}
+
+	/**
+	 * Returns all Positions of the given position category.
+	 * 
+	 * @exception BadPositionCategoryException
+	 *                If category is not defined for the document
+	 */
+	public Position[] getPositions(String category) throws org.eclipse.jface.text.BadPositionCategoryException {
+		return getPositionManager().getPositions(category);
+	}
+
+	/**
+	 * @see IDocument#getPositionUpdaters
+	 */
+	public IPositionUpdater[] getPositionUpdaters() {
+		return getPositionManager().getPositionUpdaters();
+	}
+
+	/**
+	 * This method can return null, which is the case if the offset is just
+	 * before or just after the existing text. Compare with
+	 * getNodeAtCharacterOffset.
+	 */
+	public IStructuredDocumentRegion getRegionAtCharacterOffset(int offset) {
+		IStructuredDocumentRegion result = null;
+
+		// FIXME: need to synch on 'cachedRegion' (but since that's a
+		// constantly changing object, we
+		// can't, so need to add a "region_lock" object, and use it here, and
+		// in re-parser.
+		// Oh, and need to make sure, after synch, that the region is not
+		// deleted, and if so, I guess go back
+		// to the beginning!
+
+		// cached node can be null when document is empty
+		IStructuredDocumentRegion potentialCachedRegion = getCachedDocumentRegion();
+		if (potentialCachedRegion != null) {
+
+			// 
+
+			// if we already have the right node, return that.
+			if (potentialCachedRegion.containsOffset(offset)) {
+				result = potentialCachedRegion;
+			}
+			else {
+				// first, find out what direction to go, relative to
+				// cachedNode.
+				// negative means "towards the front" of the file,
+				// postitive
+				// means
+				// towards the end.
+				int direction = offset - potentialCachedRegion.getStart();
+				if (direction < 0) {
+					// search towards beginning
+					while (!potentialCachedRegion.containsOffset(offset)) {
+						IStructuredDocumentRegion tempNode = potentialCachedRegion.getPrevious();
+						if (tempNode == null) {
+							break;
+						}
+						else {
+							potentialCachedRegion = tempNode;
+						}
+					}
+				}
+				else {
+					// search towards end
+					// There is a legitamat condition where the
+					// offset will not be contained in any node,
+					// which is if the offset is just past the last
+					// character of text.
+					// And, we must gaurd against setting cachedNode to
+					// null!
+					while (!potentialCachedRegion.containsOffset(offset)) {
+						IStructuredDocumentRegion tempNode = potentialCachedRegion.getNext();
+						if (tempNode == null)
+							break;
+						else
+							potentialCachedRegion = tempNode;
+					}
+				}
+			}
+			result = potentialCachedRegion;
+		}
+		// just to be doubly sure we never assign null to an already valid
+		// cachedRegion.
+		// I believe any time 'result' is null at this point, that just means
+		// we have an
+		// empty document, and the cachedRegion is already null, but we check
+		// and print
+		// warning, just so during development we be sure we never accidently
+		// break this assumption.
+		if (result != null)
+			setCachedDocumentRegion(result);
+		else if (getCachedDocumentRegion() != null) {
+			throw new IllegalStateException("Program Error: no region could be found to cache, but cache was non null. Indicates corrupted model or region list"); //$NON-NLS-1$
+		}
+
+		return result;
+	}
+
+	public IStructuredDocumentRegionList getRegionList() {
+		CoreNodeList result = null;
+		if (getCachedDocumentRegion() == null)
+			result = new CoreNodeList(null);
+		else
+			result = new CoreNodeList(getFirstStructuredDocumentRegion());
+
+		return result;
+	}
+
+
+	public IStructuredDocumentRegion[] getStructuredDocumentRegions() {
+		return getStructuredDocumentRegions(0, getLength());
+	}
+
+	/**
+	 * <p>
+	 * In the case of 0 length, the <code>IStructuredDocumentRegion</code>
+	 * at the character offset is returened. In other words, the region to the
+	 * right of the caret is returned. except for at the end of the document,
+	 * then the last region is returned.
+	 * </p>
+	 * <p>
+	 * Otherwise all the regions "inbetween" the indicated range are returned,
+	 * including the regions which overlap the region.
+	 * </p>
+	 * 
+	 * <br>
+	 * eg.
+	 * <p>
+	 * <br>
+	 * eg.
+	 * 
+	 * <pre>
+	 *                        &lt;html&gt;[&lt;head&gt;&lt;/head&gt;]&lt;/html&gt; returns &lt;head&gt;,&lt;/head&gt;
+	 * </pre>
+	 *    <pre>
+	 *                        &lt;ht[ml&gt;&lt;head&gt;&lt;/he]ad&gt;&lt;/html&gt; returns &lt;html&gt;,&lt;head&gt;,&lt;/head&gt;
+	 * </pre>
+	 * 
+	 * <pre>
+	 *                          &lt;html&gt;[&lt;head&gt;&lt;/head&gt;]&lt;/html&gt; returns &lt;head&gt;,&lt;/head&gt;
+	 * </pre>
+	 *    <pre>
+	 *                          &lt;ht[ml&gt;&lt;head&gt;&lt;/he]ad&gt;&lt;/html&gt; returns &lt;html&gt;,&lt;head&gt;,&lt;/head&gt;
+	 * </pre>
+	 * 
+	 * </p>
+	 */
+	public IStructuredDocumentRegion[] getStructuredDocumentRegions(int start, int length) {
+
+		if (length < 0)
+			throw new IllegalArgumentException("can't have negative length"); //$NON-NLS-1$
+
+		// this will make the right edge of the range point into the selection
+		// eg. <html>[<head></head>]</html>
+		// will return <head>,</head> instead of <head>,</head>,</html>
+		if (length > 0)
+			length--;
+
+		List results = new ArrayList();
+
+		// start thread safe block
+		try {
+			acquireLock();
+
+			IStructuredDocumentRegion currentRegion = getRegionAtCharacterOffset(start);
+			IStructuredDocumentRegion endRegion = getRegionAtCharacterOffset(start + length);
+			while (currentRegion != endRegion && currentRegion != null) {
+				results.add(currentRegion);
+				currentRegion = currentRegion.getNext();
+			}
+			// need to add that last end region
+			// can be null in the case of an empty document
+			if (endRegion != null)
+				results.add(endRegion);
+		}
+		finally {
+			releaseLock();
+		}
+		// end thread safe block
+
+		return (IStructuredDocumentRegion[]) results.toArray(new IStructuredDocumentRegion[results.size()]);
+	}
+
+	/**
+	 * was made public for easier testing. Normally should never be used by
+	 * client codes.
+	 */
+	public IStructuredTextReParser getReParser() {
+		if (fReParser == null) {
+			fReParser = new StructuredDocumentReParser();
+			fReParser.setStructuredDocument(this);
+		}
+		return fReParser;
+	}
+
+	private ITextStore getStore() {
+		return fStore;
+	}
+
+	public String getText() {
+		String result = get();
+		return result;
+	}
+
+	/**
+	 * Returns the document's line tracker. Assumes that the document has been
+	 * initialized with a line tracker.
+	 * 
+	 * @return the document's line tracker
+	 */
+	private ILineTracker getTracker() {
+		return fTracker;
+	}
+
+	public IStructuredTextUndoManager getUndoManager() {
+		if (fUndoManager == null) {
+			fUndoManager = new StructuredTextUndoManager();
+		}
+		return fUndoManager;
+	}
+
+	void initializeFirstAndLastDocumentRegion() {
+		// cached Node must also be first, at the initial point. Only
+		// valid
+		// to call this method right after the first parse.
+		// 
+		// when starting afresh, our cachedNode should be our firstNode,
+		// so be sure to initialize the firstNode
+		firstDocumentRegion = getCachedDocumentRegion();
+		// be sure to use 'getNext' for this initial finding of the last
+		// node,
+		// since the implementation of node.getLastNode may simply call
+		// structuredDocument.getLastStructuredDocumentRegion!
+		IStructuredDocumentRegion aNode = firstDocumentRegion;
+		if (aNode == null) {
+			// defect 254607: to handle empty documents right, if
+			// firstnode is
+			// null, make sure last node is null too
+			lastDocumentRegion = null;
+		}
+		else {
+			while (aNode != null) {
+				lastDocumentRegion = aNode;
+				aNode = aNode.getNext();
+			}
+		}
+	}
+
+	/**
+	 * @see IDocument#insertPositionUpdater
+	 */
+	public void insertPositionUpdater(IPositionUpdater updater, int index) {
+		getPositionManager().insertPositionUpdater(updater, index);
+	}
+
+	private void internal_addPositionCategory(String category) {
+		getPositionManager().addPositionCategory(category);
+	}
+
+	private void internal_addPositionUpdater(IPositionUpdater updater) {
+		getPositionManager().addPositionUpdater(updater);
+	}
+
+	private void internal_setParser(RegionParser newParser) {
+		fParser = newParser;
+	}
+
+	String internalGet(int offset, int length) {
+		String result = null;
+		// int myLength = getLength();
+		// if ((0 > offset) || (0 > length) || (offset + length > myLength))
+		// throw new BadLocationException();
+		result = getStore().get(offset, length);
+		return result;
+	}
+
+	/**
+	 * @param requester
+	 * @param start
+	 * @param replacementLength
+	 * @param changes
+	 * @param modificationStamp
+	 * @param ignoreReadOnlySettings
+	 * @return
+	 */
+	private StructuredDocumentEvent internalReplaceText(Object requester, int start, int replacementLength, String changes, long modificationStamp, boolean ignoreReadOnlySettings) {
+		StructuredDocumentEvent result = null;
+
+		stopPostNotificationProcessing();
+		if (changes == null)
+			changes = ""; //$NON-NLS-1$
+		// 
+		if (Debug.debugStructuredDocument)
+			System.out.println(getClass().getName() + "::replaceText(" + start + "," + replacementLength + "," + changes + ")"); //$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
+		if (Debug.perfTestStructuredDocumentOnly || Debug.perfTest || Debug.perfTestRawStructuredDocumentOnly) {
+			startStreamTime = System.currentTimeMillis();
+		}
+		try {
+			// Note: event must be computed before 'fire' method called
+			fDocumentEvent = new DocumentEvent(this, start, replacementLength, changes);
+			fireDocumentAboutToChanged();
+
+			try {
+				acquireLock();
+
+				if (!ignoreReadOnlySettings && (containsReadOnly(start, replacementLength))) {
+					NoChangeEvent noChangeEvent = new NoChangeEvent(this, requester, changes, start, replacementLength);
+					noChangeEvent.reason = NoChangeEvent.READ_ONLY_STATE_CHANGE;
+					result = noChangeEvent;
+				}
+				else {
+					result = updateModel(requester, start, replacementLength, changes);
+				}
+			}
+			finally {
+				releaseLock();
+			}
+
+
+			if (Debug.perfTestRawStructuredDocumentOnly || Debug.perfTest) {
+				long stopStreamTime = System.currentTimeMillis();
+				System.out.println("\n\t\t\t\t Time for IStructuredDocument raw replaceText: " + (stopStreamTime - startStreamTime)); //$NON-NLS-1$
+			}
+			if (Debug.debugStructuredDocument) {
+				System.out.println("event type returned by replaceTextWithNoDebuggingThread: " + result); //$NON-NLS-1$
+			}
+		}
+		finally {
+			// FUTURE_TO_DO: implement callback mechanism? to avoid instanceof
+			// and casting
+			// fireStructuredDocumentEvent must be called in order to end
+			// documentAboutToBeChanged state
+
+
+			// increment modification stamp if modifications were made
+			if (result != null && !(result instanceof NoChangeEvent)) {
+				fModificationStamp= modificationStamp;
+				fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
+				fDocumentEvent.fModificationStamp = fModificationStamp;
+			}
+				
+			if (result == null) {
+				// result should not be null, but if an exception was thrown,
+				// it will be
+				// so send a noChangeEvent and log the problem
+				NoChangeEvent noChangeEvent = new NoChangeEvent(this, requester, changes, start, replacementLength);
+				noChangeEvent.reason = NoChangeEvent.NO_EVENT;
+				fireStructuredDocumentEvent(noChangeEvent);
+				Logger.log(Logger.ERROR, "Program Error: invalid structured document event"); //$NON-NLS-1$
+			}
+			else {
+				if (result instanceof RegionChangedEvent) {
+					fireStructuredDocumentEvent((RegionChangedEvent) result);
+				}
+				else {
+					if (result instanceof RegionsReplacedEvent) {
+						fireStructuredDocumentEvent((RegionsReplacedEvent) result);
+					}
+					else {
+						if (result instanceof StructuredDocumentRegionsReplacedEvent) {
+							// probably more efficient to mark old regions as
+							// 'deleted' at the time
+							// that are determined to be deleted, but I'll do
+							// here
+							// in then central spot
+							// for programming ease.
+							updateDeletedFields((StructuredDocumentRegionsReplacedEvent) result);
+							fireStructuredDocumentEvent((StructuredDocumentRegionsReplacedEvent) result);
+						}
+						else {
+							if (result instanceof NoChangeEvent) {
+								fireStructuredDocumentEvent((NoChangeEvent) result);
+							}
+							else {
+								// if here, this means a new event was created
+								// and not handled here
+								// just send a no event until this issue is
+								// resolved.
+								NoChangeEvent noChangeEvent = new NoChangeEvent(this, requester, changes, start, replacementLength);
+								noChangeEvent.reason = NoChangeEvent.NO_EVENT;
+								fireStructuredDocumentEvent(noChangeEvent);
+								Logger.log(Logger.INFO, "Program Error: unexpected structured document event: " + result); //$NON-NLS-1$
+							}
+						}
+					}
+				}
+			}
+
+			if (Debug.perfTest || Debug.perfTestStructuredDocumentOnly) {
+				long stopStreamTime = System.currentTimeMillis();
+				System.out.println("\n\t\t\t\t Total Time for IStructuredDocument event signaling/processing in replaceText: " + (stopStreamTime - startStreamTime)); //$NON-NLS-1$
+			}
+			resumePostNotificationProcessing();
+		}
+		return result;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.CharSequence#length()
+	 */
+	public int length() {
+
+		return getLength();
+	}
+	
+	public void makeReadOnly(int startOffset, int length) {
+		makeReadOnly(startOffset, length, false, false);
+	}
+
+	public void makeReadOnly(int startOffset, int length, boolean canInsertBefore, boolean canInsertAfter) {	
+		// doesn't make sense to have a readonly region of 0 length,
+		// so we'll ignore those requests
+		if (length <= 0)
+			return;
+		String affectedText = this.get(startOffset, length);
+		// a document event for "read only" change ... must
+		// be followed by "no change" structuredDocument event
+		// fDocumentEvent = new DocumentEvent(this, startOffset, length,
+		// affectedText);
+		fireReadOnlyAboutToBeChanged();
+		// if (containsReadOnly(startOffset, length)) {
+		// adjustReadOnlyRegions(startOffset, length);
+		// } else {
+		// we can blindly add category, since no harm done if already
+		// exists.
+		addPositionCategory(READ_ONLY_REGIONS_CATEGORY);
+		Position newPosition = new ReadOnlyPosition(startOffset, length, canInsertBefore);
+		try {
+			addPosition(READ_ONLY_REGIONS_CATEGORY, newPosition);
+			// FIXME: need to change API to pass in requester, so this event
+			// can be
+			// created correctly, instead of using null.
+			NoChangeEvent noChangeEvent = new NoChangeEvent(this, null, affectedText, startOffset, length);
+			noChangeEvent.reason = NoChangeEvent.READ_ONLY_STATE_CHANGE;
+			fireReadOnlyStructuredDocumentEvent(noChangeEvent);
+		}
+		catch (BadLocationException e) {
+			// for now, log and ignore. Perhaps later we
+			// could adjust to handle some cases?
+			Logger.logException(("could not create readonly region at " + startOffset + " to " + length), e); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		catch (BadPositionCategoryException e) {
+			// should never occur, since we add category
+			Logger.logException(e);
+		}
+	}
+
+	public IStructuredDocument newInstance() {
+		IStructuredDocument newInstance = StructuredDocumentFactory.getNewStructuredDocumentInstance(getParser().newInstance());
+		((BasicStructuredDocument) newInstance).setReParser(getReParser().newInstance());
+		if (getDocumentPartitioner() instanceof StructuredTextPartitioner) {
+			newInstance.setDocumentPartitioner(((StructuredTextPartitioner) getDocumentPartitioner()).newInstance());
+			newInstance.getDocumentPartitioner().connect(newInstance);
+		}
+		newInstance.setLineDelimiter(getLineDelimiter());
+		if (getEncodingMemento() != null) {
+			newInstance.setEncodingMemento((EncodingMemento) getEncodingMemento().clone());
+		}
+		return newInstance;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.wst.sse.core.internal.text.IRegionComparible#regionMatches(int,
+	 *      int, java.lang.String)
+	 */
+	public boolean regionMatches(int offset, int length, String stringToCompare) {
+		boolean result = false;
+		ITextStore store = getStore();
+		if (store instanceof IRegionComparible) {
+			result = ((IRegionComparible) store).regionMatches(offset, length, stringToCompare);
+		}
+		else {
+			result = get(offset, length).equals(stringToCompare);
+		}
+		return result;
+	}
+
+	public boolean regionMatchesIgnoreCase(int offset, int length, String stringToCompare) {
+		boolean result = false;
+		ITextStore store = getStore();
+		if (store instanceof IRegionComparible) {
+			result = ((IRegionComparible) store).regionMatchesIgnoreCase(offset, length, stringToCompare);
+		}
+		else {
+			result = get(offset, length).equalsIgnoreCase(stringToCompare);
+		}
+		return result;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension#registerPostNotificationReplace(org.eclipse.jface.text.IDocumentListener,
+	 *      org.eclipse.jface.text.IDocumentExtension.IReplace)
+	 */
+	public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+		if (fAcceptPostNotificationReplaces) {
+			if (fPostNotificationChanges == null)
+				fPostNotificationChanges = new ArrayList(1);
+			fPostNotificationChanges.add(new RegisteredReplace(owner, replace));
+		}
+	}
+
+	protected void releaseLock() {
+		// do nothing here in super class
+	}
+
+	public void removeDocumentAboutToChangeListener(IModelAboutToBeChangedListener listener) {
+		synchronized (listenerLock) {
+
+			if ((fStructuredDocumentAboutToChangeListeners != null) && (listener != null)) {
+				// if its not in the listeners, we'll ignore the request
+				if (Utilities.contains(fStructuredDocumentAboutToChangeListeners, listener)) {
+					int oldSize = fStructuredDocumentAboutToChangeListeners.length;
+					int newSize = oldSize - 1;
+					Object[] newListeners = new Object[newSize];
+					int index = 0;
+					for (int i = 0; i < oldSize; i++) {
+						if (fStructuredDocumentAboutToChangeListeners[i] == listener) { // ignore
+						}
+						else {
+							// copy old to new if its not the one we are
+							// removing
+							newListeners[index++] = fStructuredDocumentAboutToChangeListeners[i];
+						}
+					}
+					// now that we have a new array, let's switch it for the
+					// old
+					// one
+					fStructuredDocumentAboutToChangeListeners = newListeners;
+				}
+			}
+		}
+	}
+
+	/**
+	 * removeModelChangedListener method comment.
+	 */
+	public void removeDocumentChangedListener(IStructuredDocumentListener listener) {
+		synchronized (listenerLock) {
+
+			if ((fStructuredDocumentChangedListeners != null) && (listener != null)) {
+				// if its not in the listeners, we'll ignore the request
+				if (Utilities.contains(fStructuredDocumentChangedListeners, listener)) {
+					int oldSize = fStructuredDocumentChangedListeners.length;
+					int newSize = oldSize - 1;
+					Object[] newListeners = new Object[newSize];
+					int index = 0;
+					for (int i = 0; i < oldSize; i++) {
+						if (fStructuredDocumentChangedListeners[i] == listener) { // ignore
+						}
+						else {
+							// copy old to new if its not the one we are
+							// removing
+							newListeners[index++] = fStructuredDocumentChangedListeners[i];
+						}
+					}
+					// now that we have a new array, let's switch it for the
+					// old
+					// one
+					fStructuredDocumentChangedListeners = newListeners;
+				}
+			}
+		}
+	}
+
+	public void removeDocumentChangingListener(IStructuredDocumentListener listener) {
+		synchronized (listenerLock) {
+
+			if ((fStructuredDocumentChangingListeners != null) && (listener != null)) {
+				// if its not in the listeners, we'll ignore the request
+				if (Utilities.contains(fStructuredDocumentChangingListeners, listener)) {
+					int oldSize = fStructuredDocumentChangingListeners.length;
+					int newSize = oldSize - 1;
+					Object[] newListeners = new Object[newSize];
+					int index = 0;
+					for (int i = 0; i < oldSize; i++) {
+						if (fStructuredDocumentChangingListeners[i] == listener) { // ignore
+						}
+						else {
+							// copy old to new if its not the one we are
+							// removing
+							newListeners[index++] = fStructuredDocumentChangingListeners[i];
+						}
+					}
+					// now that we have a new array, let's switch it for the
+					// old
+					// one
+					fStructuredDocumentChangingListeners = newListeners;
+				}
+			}
+		}
+	}
+
+	public void removeDocumentListener(IDocumentListener listener) {
+		synchronized (listenerLock) {
+
+			if ((fDocumentListeners != null) && (listener != null)) {
+				// if its not in the listeners, we'll ignore the request
+				if (Utilities.contains(fDocumentListeners, listener)) {
+					int oldSize = fDocumentListeners.length;
+					int newSize = oldSize - 1;
+					IDocumentListener[] newListeners = new IDocumentListener[newSize];
+					int index = 0;
+					for (int i = 0; i < oldSize; i++) {
+						if (fDocumentListeners[i] == listener) { // ignore
+						}
+						else {
+							// copy old to new if its not the one we are
+							// removing
+							newListeners[index++] = fDocumentListeners[i];
+						}
+					}
+					// now that we have a new array, let's switch it for the
+					// old
+					// one
+					fDocumentListeners = newListeners;
+				}
+			}
+		}
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.IDocument#removeDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
+	 */
+	public void removeDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+		synchronized (listenerLock) {
+
+			Assert.isNotNull(listener);
+			if (fDocumentPartitioningListeners != null)
+				fDocumentPartitioningListeners.remove(listener);
+		}
+	}
+
+	/**
+	 * Removes the given <code>Position</code> from the document's default
+	 * position category. The default position category is to be defined by
+	 * the implementers. If the position is not part of the document's default
+	 * category nothing happens.
+	 */
+	public void removePosition(Position position) {
+		getPositionManager().removePosition(position);
+	}
+
+	/**
+	 * @see IDocument#removePosition
+	 * @exception BadPositionCategoryException
+	 *                If the category is not defined for the document
+	 */
+	public void removePosition(String category, Position position) throws BadPositionCategoryException {
+		getPositionManager().removePosition(category, position);
+	}
+
+	/**
+	 * @see IDocument#removePositionCategory
+	 * @exception BadPositionCategoryException
+	 *                If the category is not defined for the document
+	 */
+	public void removePositionCategory(String category) throws BadPositionCategoryException {
+		getPositionManager().removePositionCategory(category);
+	}
+
+	/**
+	 * @see IDocument#removePositionUpdater
+	 */
+	public void removePositionUpdater(IPositionUpdater updater) {
+		getPositionManager().removePositionUpdater(updater);
+	}
+
+	/**
+	 * Removes the given document listener from teh document's list of
+	 * prenotified document listeners. If the listener is not registered with
+	 * the document nothing happens.
+	 * <p>
+	 * 
+	 * This method is not for public use, it may only be called by
+	 * implementers of <code>IDocumentAdapter</code> and only if those
+	 * implementers need to implement <code>IDocumentListener</code>.
+	 * 
+	 * @param documentAdapter
+	 *            the listener to be removed
+	 * 
+	 * @see #addPrenotifiedDocumentListener(IDocumentListener)
+	 */
+	public void removePrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener documentAdapter) {
+		synchronized (listenerLock) {
+
+			if (Utilities.contains(fPrenotifiedDocumentListeners, documentAdapter)) {
+				int previousSize = fPrenotifiedDocumentListeners.length;
+				if (previousSize > 1) {
+					IDocumentListener[] listeners = new IDocumentListener[previousSize - 1];
+					int previousIndex = 0;
+					int newIndex = 0;
+					while (previousIndex < previousSize) {
+						if (fPrenotifiedDocumentListeners[previousIndex] != documentAdapter)
+							listeners[newIndex++] = fPrenotifiedDocumentListeners[previousIndex];
+						previousIndex++;
+					}
+					fPrenotifiedDocumentListeners = listeners;
+				}
+				else {
+					fPrenotifiedDocumentListeners = null;
+				}
+			}
+		}
+	}
+
+	/**
+	 * This method is for INTERNAL USE ONLY and is NOT API.
+	 * 
+	 * Rebuilds the StructuredDocumentRegion chain from the existing text.
+	 * FileBuffer support does not allow clients to know the document's
+	 * location before the text contents are set.
+	 * 
+	 * @see set(String)
+	 */
+	public void reparse(Object requester) {
+		// check if we're already making document-wide changes on this thread
+		if (fStoppedCount > 0)
+			return;
+		
+		stopPostNotificationProcessing();
+		clearReadOnly();
+
+		try {
+			acquireLock();
+
+			CharSequenceReader subSetTextStoreReader = new CharSequenceReader((CharSequence) getStore(), 0, getStore().getLength());
+			resetParser(subSetTextStoreReader, 0);
+			//
+			setCachedDocumentRegion(getParser().getDocumentRegions());
+			// when starting afresh, our cachedNode should be our firstNode,
+			// so be sure to initialize the firstNode and lastNode
+			initializeFirstAndLastDocumentRegion();
+			StructuredDocumentRegionIterator.setParentDocument(getCachedDocumentRegion(), this);
+		}
+		finally {
+			releaseLock();
+		}
+
+		resumePostNotificationProcessing();
+	}
+
+	/**
+	 * @see IDocument#replace
+	 * @exception BadLocationException
+	 *                If position is not a valid range in the document
+	 */
+	public void replace(int offset, int length, String text) throws BadLocationException {
+		if (Debug.displayWarnings) {
+			System.out.println("Note: IStructuredDocument::replace(int, int, String) .... its better to use replaceText(source, string, int, int) API for structuredDocument updates"); //$NON-NLS-1$
+		}
+		replaceText(this, offset, length, text);
+	}
+
+	/**
+	 * Replace the text with "newText" starting at position "start" for a
+	 * length of "replaceLength".
+	 * <p>
+	 * 
+	 * @param pos
+	 *            start offset of text to replace None of the offsets include
+	 *            delimiters of preceeding lines. Offset 0 is the first
+	 *            character of the document.
+	 * @param length
+	 *            start offset of text to replace
+	 * @param text
+	 *            start offset of text to replace
+	 *            <p>
+	 *            Implementors have to notify TextChanged listeners after the
+	 *            content has been updated. The TextChangedEvent should be set
+	 *            as follows:
+	 * 
+	 * event.type = SWT.TextReplaced event.start = start of the replaced text
+	 * event.numReplacedLines = number of replaced lines event.numNewLines =
+	 * number of new lines event.replacedLength = length of the replaced text
+	 * event.newLength = length of the new text
+	 * 
+	 * NOTE: numNewLines is the number of inserted lines and numReplacedLines
+	 * is the number of deleted lines based on the change that occurs
+	 * visually. For example:
+	 * 
+	 * replacedText newText numReplacedLines numNewLines "" "\n" 0 1 "\n\n"
+	 * "a" 2 0 "a" "\n\n" 0 2
+	 */
+	/**
+	 * One of the APIs to manipulate the IStructuredDocument in terms of text.
+	 */
+	public StructuredDocumentEvent replaceText(Object requester, int pos, int length, String text) {
+		if (length == 0 && (text == null || text.length() == 0))
+			return replaceText(requester, pos, length, text, getModificationStamp(), true);
+		else
+			return replaceText(requester, pos, length, text, getNextModificationStamp(), true);
+	}
+
+	public StructuredDocumentEvent replaceText(Object requester, int start, int replacementLength, String changes, boolean ignoreReadOnlySettings) {
+		long modificationStamp;
+		
+		if (replacementLength == 0 && (changes == null || changes.length() == 0))
+			modificationStamp = getModificationStamp();
+		else
+			modificationStamp = getNextModificationStamp();
+		
+		return replaceText(requester, start, replacementLength, changes, modificationStamp, ignoreReadOnlySettings);
+	}
+	
+	private StructuredDocumentEvent replaceText(Object requester, int start, int replacementLength, String changes, long modificationStamp, boolean ignoreReadOnlySettings) {
+		StructuredDocumentEvent event = internalReplaceText(requester, start, replacementLength, changes, modificationStamp, ignoreReadOnlySettings);
+		return event;
+	}
+
+	void resetParser(int startOffset, int endOffset) {
+
+		RegionParser parser = getParser();
+		ITextStore textStore = getStore();
+		if (textStore instanceof CharSequence) {
+			CharSequenceReader subSetTextStoreReader = new CharSequenceReader((CharSequence) textStore, startOffset, endOffset - startOffset);
+			parser.reset(subSetTextStoreReader, startOffset);
+		}
+		else {
+			String newNodeText = get(startOffset, endOffset - startOffset);
+			parser.reset(newNodeText, startOffset);
+
+		}
+
+	}
+
+	void resetParser(Reader reader, int startOffset) {
+		RegionParser parser = getParser();
+		parser.reset(reader, startOffset);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension#resumePostNotificationProcessing()
+	 */
+	public void resumePostNotificationProcessing() {
+		--fStoppedCount;
+		if (fStoppedCount == 0 && fReentranceCount == 0)
+			executePostNotificationChanges();
+	}
+
+	/**
+	 * @deprecated in superclass in 3.0 - use a FindReplaceDocumentAdapter
+	 *             directly
+	 * @see IDocument#search
+	 */
+	public int search(int startPosition, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord) throws BadLocationException {
+		// (dmw) I added this warning, to know if still being used. I'm not
+		// sure it
+		// works as expected any longer.
+		// but the warning should be removed, once know.
+		Logger.log(Logger.INFO, "WARNING: using unsupported deprecated method 'search'"); //$NON-NLS-1$
+		int offset = -1;
+		IRegion match = new FindReplaceDocumentAdapter(this).find(startPosition, findString, forwardSearch, caseSensitive, wholeWord, false);
+		if (match != null) {
+			offset = match.getOffset();
+		}
+		return offset;
+	}
+
+	/**
+	 * @see IDocument#setText
+	 */
+	public void set(String string) {
+		if (Debug.displayInfo) {
+			System.out.println("Note: IStructuredDocument::setText(String) .... its better to use setText(source, string) API for structuredDocument updates"); //$NON-NLS-1$
+		}
+		setText(null, string);
+	}
+
+	/**
+	 * This may be marked public, but should be packaged protected, once
+	 * refactoring is complete (in other words, not for client use).
+	 */
+	public void setCachedDocumentRegion(IStructuredDocumentRegion structuredRegion) {
+		if (USE_LOCAL_THREAD) {
+			fCurrentDocumentRegionCache.set(structuredRegion);
+		}
+		else {
+			cachedDocumentRegion = structuredRegion;
+		}
+	}
+
+	/**
+	 * Sets the document's partitioner.
+	 * 
+	 * @see IDocumentPartitioner
+	 */
+	public void setDocumentPartitioner(IDocumentPartitioner partitioner) {
+		setDocumentPartitioner(IDocumentExtension3.DEFAULT_PARTITIONING, partitioner);
+	}
+
+
+	public void setDocumentPartitioner(String partitioning, IDocumentPartitioner partitioner) {
+		if (partitioner == null) {
+			if (fDocumentPartitioners != null) {
+				fDocumentPartitioners.remove(partitioning);
+				if (fDocumentPartitioners.size() == 0)
+					fDocumentPartitioners = null;
+			}
+		}
+		else {
+			if (fDocumentPartitioners == null)
+				fDocumentPartitioners = new HashMap();
+			fDocumentPartitioners.put(partitioning, partitioner);
+		}
+		DocumentPartitioningChangedEvent event = new DocumentPartitioningChangedEvent(this);
+		event.setPartitionChange(partitioning, 0, getLength());
+		fireDocumentPartitioningChanged(event);
+	}
+
+	public void setEncodingMemento(EncodingMemento encodingMemento) {
+		this.encodingMemento = encodingMemento;
+	}
+
+	void setFirstDocumentRegion(IStructuredDocumentRegion region) {
+		firstDocumentRegion = region;
+
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension4#setInitialLineDelimiter(java.lang.String)
+	 */
+	public void setInitialLineDelimiter(String lineDelimiter) {
+		// make sure our preferred delimiter is
+		// one of the legal ones
+		if (Utilities.containsString(getLegalLineDelimiters(), lineDelimiter)) {
+			fInitialLineDelimiter= lineDelimiter;
+		}
+		else {
+			if (Logger.DEBUG_DOCUMENT)
+				Logger.log(Logger.INFO, "Attempt to set linedelimiter to non-legal delimiter"); //$NON-NLS-1$ //$NON-NLS-2$
+			fInitialLineDelimiter = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, System.getProperty("line.separator"), new IScopeContext[] { new InstanceScope() });//$NON-NLS-1$
+		}
+	}
+
+	void setLastDocumentRegion(IStructuredDocumentRegion region) {
+		lastDocumentRegion = region;
+
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument#setLineDelimiter(java.lang.String)
+	 */
+	public void setLineDelimiter(String delimiter) {
+		setInitialLineDelimiter(delimiter);
+	}
+
+	/**
+	 * Sets the document's line tracker. Must be called at the beginning of
+	 * the constructor.
+	 * 
+	 * @param tracker
+	 *            the document's line tracker
+	 */
+	private void setLineTracker(ILineTracker tracker) {
+		Assert.isNotNull(tracker);
+		fTracker = tracker;
+	}
+
+	public void setParser(RegionParser newParser) {
+		internal_setParser(newParser);
+	}
+
+	/**
+	 * @param positionManager
+	 *            The positionManager to set.
+	 */
+	// TODO: make private is needed, else remove
+	void setPositionManager(GenericPositionManager positionManager) {
+		fPositionManager = positionManager;
+	}
+
+	/**
+	 * 
+	 */
+	public void setReParser(IStructuredTextReParser newReParser) {
+		fReParser = newReParser;
+		if (fReParser != null) {
+			fReParser.setStructuredDocument(this);
+		}
+	}
+
+	/**
+	 * One of the APIs to manipulate the IStructuredDocument in terms of text.
+	 */
+	public StructuredDocumentEvent setText(Object requester, String theString) {
+		StructuredDocumentEvent result = null;
+		result = replaceText(requester, 0, getLength(), theString, getNextModificationStamp(), true);
+		return result;
+	}
+
+	/**
+	 * Sets the document's text store. Must be called at the beginning of the
+	 * constructor.
+	 * 
+	 * @param store
+	 *            the document's text store
+	 */
+	private void setTextStore(ITextStore store) {
+		Assert.isNotNull(store);
+		fStore = store;
+	}
+
+	public void setUndoManager(IStructuredTextUndoManager undoManager) {
+
+		// if the undo manager has already been set, then
+		// fail fast, since changing the undo manager will lead
+		// to unusual results (or at least loss of undo stack).
+		if (fUndoManager != null && fUndoManager != undoManager) {
+			throw new IllegalArgumentException("can not change undo manager once its been set"); //$NON-NLS-1$
+		}
+		else {
+			fUndoManager = undoManager;
+		}
+	}
+
+
+	/*
+	 * {@inheritDoc}
+	 */
+	public void startSequentialRewrite(boolean normalized) {
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension#stopPostNotificationProcessing()
+	 */
+	public void stopPostNotificationProcessing() {
+		++fStoppedCount;
+	}
+
+
+	/*
+	 * {@inheritDoc}
+	 */
+	public void stopSequentialRewrite() {
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.CharSequence#subSequence(int, int)
+	 */
+	public CharSequence subSequence(int arg0, int arg1) {
+		return get(arg0, arg1);
+	}
+
+	/**
+	 * @param result
+	 */
+	private void updateDeletedFields(StructuredDocumentRegionsReplacedEvent event) {
+		IStructuredDocumentRegionList oldRegions = event.getOldStructuredDocumentRegions();
+		for (int i = 0; i < oldRegions.getLength(); i++) {
+			IStructuredDocumentRegion structuredDocumentRegion = oldRegions.item(i);
+			structuredDocumentRegion.setDeleted(true);
+		}
+
+	}
+
+	/**
+	 * Called by re-parser. Note: this method may be "public" but should only
+	 * be called by re-parsers in the right circumstances.
+	 */
+	public void updateDocumentData(int start, int lengthToReplace, String changes) {
+		stopPostNotificationProcessing();
+		getStore().replace(start, lengthToReplace, changes);
+		try {
+			getTracker().replace(start, lengthToReplace, changes);
+		}
+
+		catch (BadLocationException e) {
+			// should be impossible here, but will log for now
+			Logger.logException(e);
+		}
+		if (fPositionManager != null) {
+			fPositionManager.updatePositions(new DocumentEvent(this, start, lengthToReplace, changes));
+		}
+		fModificationStamp++;
+		fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
+		resumePostNotificationProcessing();
+	}
+
+	private StructuredDocumentEvent updateModel(Object requester, int start, int lengthToReplace, String changes) {
+		StructuredDocumentEvent result = null;
+		IStructuredTextReParser reParser = getReParser();
+		// initialize the IStructuredTextReParser with the standard data
+		// that's
+		// always needed
+		reParser.initialize(requester, start, lengthToReplace, changes);
+		result = reParser.reparse();
+		// if result is null at this point, then there must be an error, since
+		// even if there
+		// was no change (either disallow due to readonly, or a person pasted
+		// the same thing
+		// they had selected) then a "NoChange" event should have been fired.
+		Assert.isNotNull(result, "no structuredDocument event was created in IStructuredDocument::updateStructuredDocument"); //$NON-NLS-1$
+		return result;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument#getPreferredLineDelimiter()
+	 */
+	public String getPreferredLineDelimiter() {
+		return getDefaultLineDelimiter();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument#setPreferredLineDelimiter(java.lang.String)
+	 */
+	public void setPreferredLineDelimiter(String probableLineDelimiter) {
+		setInitialLineDelimiter(probableLineDelimiter);
+
+	}
+
+
+	/**
+	 * Class which implements the rewritable session for the SSE.
+	 * 
+	 */
+	static class StructuredDocumentRewriteSession extends DocumentRewriteSession {
+
+		/**
+		 * Creates a new session.
+		 * 
+		 * @param sessionType
+		 *            the type of this session
+		 */
+		protected StructuredDocumentRewriteSession(DocumentRewriteSessionType sessionType) {
+			super(sessionType);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension4#startRewriteSession(org.eclipse.jface.text.DocumentRewriteSessionType)
+	 */
+	public DocumentRewriteSession startRewriteSession(DocumentRewriteSessionType sessionType) throws IllegalStateException {
+		// delegate to sub-class, so UI threading is handled correctly
+		return internalStartRewriteSession(sessionType);
+	}
+
+	/**
+	 * NOT-API. Final protected so clients may call this method if needed, but
+	 * cannot override.
+	 * 
+	 * @param sessionType
+	 * @return
+	 * @throws IllegalStateException
+	 */
+	final protected DocumentRewriteSession internalStartRewriteSession(DocumentRewriteSessionType sessionType) throws IllegalStateException {
+		if (getActiveRewriteSession() != null)
+			throw new IllegalStateException("already in a rewrite session");
+
+		DocumentRewriteSession session = new StructuredDocumentRewriteSession(sessionType);
+		DocumentRewriteSessionEvent event = new DocumentRewriteSessionEvent(this, session, DocumentRewriteSessionEvent.SESSION_START);
+		fireDocumentRewriteSessionEvent(event);
+
+		ILineTracker tracker = getTracker();
+		if (tracker instanceof ILineTrackerExtension) {
+			ILineTrackerExtension extension = (ILineTrackerExtension) tracker;
+			extension.startRewriteSession(session);
+		}
+
+		startRewriteSessionOnPartitioners(session);
+
+		if (DocumentRewriteSessionType.SEQUENTIAL == sessionType)
+			startSequentialRewrite(false);
+		else if (DocumentRewriteSessionType.STRICTLY_SEQUENTIAL == sessionType)
+			startSequentialRewrite(true);
+
+		fActiveRewriteSession = session;
+		return session;
+	}
+
+	/**
+	 * Starts the given rewrite session.
+	 *
+	 * @param session the rewrite session
+	 * @since 2.0
+	 */
+	final void startRewriteSessionOnPartitioners(DocumentRewriteSession session) {
+		if (fDocumentPartitioners != null) {
+			Iterator e= fDocumentPartitioners.values().iterator();
+			while (e.hasNext()) {
+				Object partitioner= e.next();
+				if (partitioner instanceof IDocumentPartitionerExtension3) {
+					IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
+					extension.startRewriteSession(session);
+				}
+			}
+		}
+	}
+
+
+	public void stopRewriteSession(DocumentRewriteSession session) {
+		// delegate to sub-class, so UI threading is handled correctly
+		internalStopRewriteSession(session);
+	}
+
+	/**
+	 * NOT-API. Final protected so clients may call this method if needed, but
+	 * cannot override.
+	 * 
+	 * @param session
+	 */
+	final protected void internalStopRewriteSession(DocumentRewriteSession session) {
+		if (fActiveRewriteSession == session) {
+			DocumentRewriteSessionType sessionType = session.getSessionType();
+			if (DocumentRewriteSessionType.SEQUENTIAL == sessionType || DocumentRewriteSessionType.STRICTLY_SEQUENTIAL == sessionType)
+				stopSequentialRewrite();
+
+			stopRewriteSessionOnPartitioners(session);
+
+			ILineTracker tracker = getTracker();
+			if (tracker instanceof ILineTrackerExtension) {
+				ILineTrackerExtension extension = (ILineTrackerExtension) tracker;
+				extension.stopRewriteSession(session, get());
+			}
+
+			fActiveRewriteSession = null;
+			DocumentRewriteSessionEvent event = new DocumentRewriteSessionEvent(this, session, DocumentRewriteSessionEvent.SESSION_STOP);
+			fireDocumentRewriteSessionEvent(event);
+		}
+	}
+
+	/**
+	 * Stops the given rewrite session.
+	 *
+	 * @param session the rewrite session
+	 * @since 2.0
+	 */
+	final void stopRewriteSessionOnPartitioners(DocumentRewriteSession session) {
+		if (fDocumentPartitioners != null) {
+			DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
+			Iterator e= fDocumentPartitioners.keySet().iterator();
+			while (e.hasNext()) {
+				String partitioning= (String) e.next();
+				IDocumentPartitioner partitioner= (IDocumentPartitioner) fDocumentPartitioners.get(partitioning);
+				if (partitioner instanceof IDocumentPartitionerExtension3) {
+					IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
+					extension.stopRewriteSession(session);
+					event.setPartitionChange(partitioning, 0, getLength());
+				}
+			}
+			if (!event.isEmpty())
+				fireDocumentPartitioningChanged(event);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension4#getActiveRewriteSession()
+	 */
+	public DocumentRewriteSession getActiveRewriteSession() {
+		return fActiveRewriteSession;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension4#addDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
+	 */
+	public void addDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
+		synchronized (listenerLock) {
+			Assert.isNotNull(listener);
+			if (fDocumentRewriteSessionListeners == null) {
+				fDocumentRewriteSessionListeners = new ArrayList(1);
+			}
+			if (!fDocumentRewriteSessionListeners.contains(listener))
+				fDocumentRewriteSessionListeners.add(listener);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension4#removeDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
+	 */
+	public void removeDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
+		synchronized (listenerLock) {
+
+			Assert.isNotNull(listener);
+			if (fDocumentRewriteSessionListeners != null)
+				fDocumentRewriteSessionListeners.remove(listener);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension4#replace(int, int,
+	 *      java.lang.String, long)
+	 */
+	public void replace(int offset, int length, String text, long modificationStamp) throws BadLocationException {
+		replaceText(this, offset, length, text, modificationStamp, true);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension4#set(java.lang.String,
+	 *      long)
+	 */
+	public void set(String text, long modificationStamp) {
+		// bug 151069 - overwrite read only regions when setting entire document
+		 replaceText(null, 0, getLength(), text, modificationStamp, true);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.text.IDocumentExtension4#getModificationStamp()
+	 */
+	public long getModificationStamp() {
+		return fModificationStamp;
+	}
+	
+	private long getNextModificationStamp() {
+		if (fNextModificationStamp == Long.MAX_VALUE || fNextModificationStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP)
+			fNextModificationStamp= 0;
+		else
+			fNextModificationStamp= fNextModificationStamp + 1;
+
+		return fNextModificationStamp;
+	}
+
+	/**
+	 * Fires an event, as specified, to the associated listeners.
+	 * 
+	 * @param event
+	 *            The event to fire, either a start or stop event.
+	 */
+	private void fireDocumentRewriteSessionEvent(final DocumentRewriteSessionEvent event) {
+		if (fDocumentRewriteSessionListeners == null || fDocumentRewriteSessionListeners.size() == 0)
+			return;
+
+		Object[] listeners = fDocumentRewriteSessionListeners.toArray();
+		for (int i = 0; i < listeners.length; i++) {
+			final IDocumentRewriteSessionListener l = (IDocumentRewriteSessionListener) listeners[i];
+			SafeRunner.run(new ISafeRunnable() {
+				public void run() throws Exception {
+					l.documentRewriteSessionChanged(event);
+				}
+				public void handleException(Throwable exception) {
+					// logged for us
+				}
+			});
+		}
+	}
+}
diff --git a/bundles/org.eclipse.wst.sse.ui/src-tasktags/org/eclipse/wst/sse/ui/internal/preferences/ui/MainTab.java b/bundles/org.eclipse.wst.sse.ui/src-tasktags/org/eclipse/wst/sse/ui/internal/preferences/ui/MainTab.java
new file mode 100644
index 0000000..864fd02
--- /dev/null
+++ b/bundles/org.eclipse.wst.sse.ui/src-tasktags/org/eclipse/wst/sse/ui/internal/preferences/ui/MainTab.java
@@ -0,0 +1,453 @@
+/*******************************************************************************
+ * Copyright (c) 2001, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *	   David Schneider, david.schneider@unisys.com - [142500] WTP properties pages fonts don't follow Eclipse preferences
+ *******************************************************************************/
+package org.eclipse.wst.sse.ui.internal.preferences.ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IPreferencesService;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableLayout;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.wst.sse.core.internal.provisional.tasks.TaskTag;
+import org.eclipse.wst.sse.core.internal.tasks.TaskTagPreferenceKeys;
+import org.eclipse.wst.sse.core.utils.StringUtils;
+import org.eclipse.wst.sse.ui.internal.SSEUIMessages;
+
+import com.ibm.icu.util.StringTokenizer;
+
+class MainTab implements IPreferenceTab {
+	public class TaskTagDialog extends Dialog {
+		public TaskTag taskTag = null;
+
+		Combo priorityCombo = null;
+		Text tagText = null;
+
+		public TaskTagDialog(Shell parentShell) {
+			super(parentShell);
+			setShellStyle(getShellStyle() | SWT.RESIZE);
+		}
+
+		protected void configureShell(Shell newShell) {
+			super.configureShell(newShell);
+			newShell.setText(SSEUIMessages.TaskTagPreferenceTab_5); //$NON-NLS-1$
+		}
+
+		protected Control createButtonBar(Composite parent) {
+			Control c = super.createButtonBar(parent);
+			getButton(IDialogConstants.OK_ID).setEnabled(taskTag == null || taskTag.getTag().length() > 0);
+			return c;
+		}
+
+		protected Control createDialogArea(Composite parent) {
+			Composite composite = new Composite(parent, SWT.NONE);
+			composite.setLayout(new GridLayout(2, false));
+			GridData gridData = new GridData(GridData.FILL_BOTH);
+			gridData.widthHint = parent.getDisplay().getClientArea().width / 5;
+			composite.setLayoutData(gridData);
+			Label label = new Label(composite, SWT.NONE);
+			label.setText(SSEUIMessages.TaskTagPreferenceTab_6); //$NON-NLS-1$
+			label.setLayoutData(new GridData());
+			tagText = new Text(composite, SWT.BORDER);
+			tagText.setText(taskTag != null ? taskTag.getTag() : ""); //$NON-NLS-1$
+			tagText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+			tagText.addModifyListener(new ModifyListener() {
+				public void modifyText(ModifyEvent e) {
+					String testTag = tagText.getText();
+					String[] tags = new String[fTaskTags.length];
+					for (int i = 0; i < tags.length; i++) {
+						tags[i] = fTaskTags[i].getTag();
+					}
+					getButton(IDialogConstants.OK_ID).setEnabled(tagText.getText().length() > 0 && !Arrays.asList(tags).contains(testTag));
+					taskTag = new TaskTag(tagText.getText(), priorityCombo.getSelectionIndex());
+				}
+			});
+
+			label = new Label(composite, SWT.NONE);
+			label.setText(SSEUIMessages.TaskTagPreferenceTab_7); //$NON-NLS-1$
+			label.setLayoutData(new GridData());
+			priorityCombo = new Combo(composite, SWT.READ_ONLY | SWT.SINGLE);
+			priorityCombo.setItems(new String[]{SSEUIMessages.TaskTagPreferenceTab_0, SSEUIMessages.TaskTagPreferenceTab_1, SSEUIMessages.TaskTagPreferenceTab_2});
+			priorityCombo.select(taskTag != null ? taskTag.getPriority() : TaskTag.PRIORITY_NORMAL);
+			priorityCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+			priorityCombo.addSelectionListener(new SelectionAdapter() {
+				public void widgetSelected(SelectionEvent e) {
+					taskTag = new TaskTag(taskTag.getTag(), priorityCombo.getSelectionIndex());
+				}
+			});
+		    Dialog.applyDialogFont(parent);
+			return composite;
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.jface.dialogs.Dialog#okPressed()
+		 */
+		protected void okPressed() {
+			taskTag = new TaskTag(tagText.getText(), priorityCombo.getSelectionIndex());
+			super.okPressed();
+		}
+	}
+
+	public class TaskTagTableLabelProvider extends LabelProvider implements ITableLabelProvider {
+		public TaskTagTableLabelProvider() {
+			super();
+		}
+
+		public Image getColumnImage(Object element, int columnIndex) {
+			return null;
+		}
+
+		public String getColumnText(Object element, int columnIndex) {
+			if (columnIndex < 1)
+				return ((TaskTag) element).getTag();
+			for (int i = 0; i < fTaskTags.length; i++) {
+				if (fTaskTags[i].equals(element)) {
+					if (fTaskTags[i].getPriority() == IMarker.PRIORITY_LOW) {
+						return SSEUIMessages.TaskTagPreferenceTab_0; //$NON-NLS-1$
+					}
+					else if (fTaskTags[i].getPriority() == IMarker.PRIORITY_HIGH) {
+						return SSEUIMessages.TaskTagPreferenceTab_2; //$NON-NLS-1$
+					}
+					else {
+						return SSEUIMessages.TaskTagPreferenceTab_1; //$NON-NLS-1$
+					}
+				}
+			}
+			return SSEUIMessages.TaskTagPreferenceTab_3; //$NON-NLS-1$
+		}
+	}
+
+	private static final boolean _debugPreferences = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/tasks/preferences")); //$NON-NLS-1$ //$NON-NLS-2$
+
+	private Control fControl;
+
+	private TaskTag[] fOriginalTaskTags;
+	private TaskTagPreferencePage fOwner = null;
+	private IScopeContext[] fPreferencesLookupOrder = null;
+
+	private IPreferencesService fPreferencesService = null;
+
+	private TaskTag[] fTaskTags;
+
+	private TableViewer valueTable = null;
+
+
+	public MainTab(TaskTagPreferencePage parent, IPreferencesService preferencesService, IScopeContext[] lookupOrder) {
+		super();
+		fOwner = parent;
+		fPreferencesLookupOrder = lookupOrder;
+		fPreferencesService = preferencesService;
+	}
+
+
+	private void addTag() {
+		TaskTagDialog dlg = new TaskTagDialog(fControl.getShell());
+		int result = dlg.open();
+		if (result == Window.OK) {
+			TaskTag newTag = dlg.taskTag;
+			List newTags = new ArrayList(Arrays.asList(fTaskTags));
+			newTags.add(newTag);
+			fTaskTags = (TaskTag[]) newTags.toArray(new TaskTag[newTags.size()]);
+			valueTable.setInput(fTaskTags);
+			valueTable.getTable().setSelection(fTaskTags.length - 1);
+		}
+	}
+
+	public Control createContents(Composite tabFolder) {
+		loadPreferenceValues();
+		fOriginalTaskTags = fTaskTags;
+
+		Composite composite = new Composite(tabFolder, SWT.NONE);
+		composite.setLayout(new GridLayout(2, false));
+		fControl = composite;
+
+		Label description = new Label(composite, SWT.NONE);
+		description.setText(SSEUIMessages.TaskTagPreferenceTab_33); //$NON-NLS-1$
+//		description.setBackground(composite.getBackground());
+		GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=104403
+		Point sizeHint = description.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+		gd.widthHint = sizeHint.x;
+		description.setLayoutData(gd);
+
+		valueTable = new TableViewer(composite, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL);
+		valueTable.getControl().setLayoutData(new GridData(GridData.FILL_BOTH));
+		TableColumn textColumn = new TableColumn(valueTable.getTable(), SWT.NONE, 0);
+		textColumn.setText(SSEUIMessages.TaskTagPreferenceTab_12); //$NON-NLS-1$
+		TableColumn priorityColumn = new TableColumn(valueTable.getTable(), SWT.NONE, 1);
+		priorityColumn.setText(SSEUIMessages.TaskTagPreferenceTab_13); //$NON-NLS-1$
+		valueTable.setContentProvider(new ArrayContentProvider());
+		valueTable.setLabelProvider(new TaskTagTableLabelProvider());
+		valueTable.getTable().setLinesVisible(true);
+		valueTable.getTable().setHeaderVisible(true);
+		TableLayout layout = new TableLayout();
+		layout.addColumnData(new ColumnWeightData(1, 140, true));
+		layout.addColumnData(new ColumnWeightData(1, 140, true));
+		valueTable.getTable().setLayout(layout);
+
+		Composite buttons = new Composite(composite, SWT.NONE);
+		buttons.setLayout(new GridLayout());
+		buttons.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+
+		final Button addButton = new Button(buttons, SWT.PUSH);
+		addButton.setText(SSEUIMessages.TaskTagPreferenceTab_14); //$NON-NLS-1$
+		addButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
+		final Button editButton = new Button(buttons, SWT.PUSH);
+		editButton.setText(SSEUIMessages.TaskTagPreferenceTab_15); //$NON-NLS-1$
+		editButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
+		final Button removeButton = new Button(buttons, SWT.PUSH);
+		removeButton.setText(SSEUIMessages.TaskTagPreferenceTab_16); //$NON-NLS-1$
+		removeButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
+
+		editButton.setEnabled(false);
+		removeButton.setEnabled(false);
+
+		Label warning = new Label(composite, SWT.NONE);
+		warning.setLayoutData(new GridData());
+		warning.setText(SSEUIMessages.TaskTagPreferenceTab_19); //$NON-NLS-1$
+
+		final ISelectionChangedListener selectionChangedListener = new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				ISelection selection = valueTable.getSelection();
+				editButton.setEnabled(((IStructuredSelection) selection).size() == 1);
+				removeButton.setEnabled(!selection.isEmpty());
+			}
+		};
+		valueTable.addPostSelectionChangedListener(selectionChangedListener);
+		addButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				addTag();
+				valueTable.getTable().setSelection(fTaskTags.length - 1);
+				selectionChangedListener.selectionChanged(null);
+			}
+		});
+		editButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				int i = valueTable.getTable().getSelectionIndex();
+				editTag(i);
+				if (i >= 0) {
+					valueTable.getTable().setSelection(i);
+					selectionChangedListener.selectionChanged(null);
+				}
+			}
+		});
+		removeButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				int i = valueTable.getTable().getSelectionIndex();
+				removeTags(valueTable.getSelection());
+				if (i >= 0 && i < fTaskTags.length) {
+					valueTable.getTable().setSelection(i);
+				}
+			}
+		});
+		valueTable.addDoubleClickListener(new IDoubleClickListener() {
+			public void doubleClick(DoubleClickEvent event) {
+				int i = valueTable.getTable().getSelectionIndex();
+				editTag(i);
+				if (i >= 0) {
+					valueTable.getTable().setSelection(i);
+					selectionChangedListener.selectionChanged(null);
+				}
+			}
+		});
+
+		valueTable.setInput(fTaskTags);
+
+		return composite;
+	}
+
+	/**
+	 * @param selection
+	 */
+	private void editTag(int i) {
+		if (i < 0) {
+			return;
+		}
+
+		int selection = valueTable.getTable().getSelectionIndex();
+		TaskTagDialog dlg = new TaskTagDialog(fControl.getShell());
+		dlg.taskTag = fTaskTags[selection];
+		int result = dlg.open();
+		if (result == Window.OK) {
+			fTaskTags[selection] = dlg.taskTag;
+			valueTable.refresh();
+		}
+	}
+
+	public String getTitle() {
+		return SSEUIMessages.TaskTagPreferenceTab_20;
+	}
+
+	private void loadPreferenceValues() {
+		String tags = fPreferencesService.getString(TaskTagPreferenceKeys.TASK_TAG_NODE, TaskTagPreferenceKeys.TASK_TAG_TAGS, "", fPreferencesLookupOrder); //$NON-NLS-1$
+		String priorities = fPreferencesService.getString(TaskTagPreferenceKeys.TASK_TAG_NODE, TaskTagPreferenceKeys.TASK_TAG_PRIORITIES, "", fPreferencesLookupOrder); //$NON-NLS-1$
+		loadTagsAndPrioritiesFrom(tags, priorities);
+	}
+
+	/**
+	 * @param tags
+	 * @param priorities
+	 */
+	private void loadTagsAndPrioritiesFrom(String tagString, String priorityString) {
+		String[] tags = StringUtils.unpack(tagString);
+
+		StringTokenizer toker = null;
+		List list = new ArrayList();
+
+		toker = new StringTokenizer(priorityString, ","); //$NON-NLS-1$
+		while (toker.hasMoreTokens()) {
+			Integer number = null;
+			try {
+				number = Integer.valueOf(toker.nextToken());
+			}
+			catch (NumberFormatException e) {
+				number = new Integer(IMarker.PRIORITY_NORMAL);
+			}
+			list.add(number);
+		}
+		Integer[] priorities = (Integer[]) list.toArray(new Integer[0]);
+
+		fTaskTags = new TaskTag[Math.min(tags.length, priorities.length)];
+		for (int i = 0; i < fTaskTags.length; i++) {
+			fTaskTags[i] = new TaskTag(tags[i], priorities[i].intValue());
+		}
+	}
+
+	public void performApply() {
+		save();
+
+		if (!Arrays.equals(fOriginalTaskTags, fTaskTags)) {
+			fOwner.requestRedetection();
+		}
+		fOriginalTaskTags = fTaskTags;
+	}
+
+	public void performDefaults() {
+		if (_debugPreferences) {
+			System.out.println("Loading defaults in " + getClass().getName()); //$NON-NLS-1$
+		}
+		final IEclipsePreferences defaultPreferences = fPreferencesLookupOrder.length > 0 ? fPreferencesLookupOrder[fPreferencesLookupOrder.length - 1].getNode(TaskTagPreferenceKeys.TASK_TAG_NODE) : null;
+		String tags = null;
+		String priorities = null;
+		if (defaultPreferences != null) {
+			tags = defaultPreferences.get(TaskTagPreferenceKeys.TASK_TAG_TAGS, null);
+			priorities = defaultPreferences.get(TaskTagPreferenceKeys.TASK_TAG_PRIORITIES, null);
+		}
+		loadTagsAndPrioritiesFrom(tags, priorities);
+		int selection = valueTable.getTable().getSelectionIndex();
+		valueTable.setInput(fTaskTags);
+		valueTable.getTable().setSelection(selection);
+	}
+
+	public void performOk() {
+		performApply();
+	}
+
+	/**
+	 * @param selection
+	 */
+	private void removeTags(ISelection selection) {
+		IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+		List taskTags = new ArrayList(Arrays.asList(fTaskTags));
+		taskTags.removeAll(structuredSelection.toList());
+		fTaskTags = (TaskTag[]) taskTags.toArray(new TaskTag[taskTags.size()]);
+		valueTable.setInput(fTaskTags);
+	}
+
+	private void save() {
+		IEclipsePreferences defaultPreferences = new DefaultScope().getNode(TaskTagPreferenceKeys.TASK_TAG_NODE);
+		String defaultTags = defaultPreferences.get(TaskTagPreferenceKeys.TASK_TAG_TAGS, null);
+		String defaultPriorities = defaultPreferences.get(TaskTagPreferenceKeys.TASK_TAG_PRIORITIES, null);
+
+		StringBuffer buf = new StringBuffer();
+		for (int i = 0; i < fTaskTags.length; i++) {
+			if (i > 0) {
+				buf.append(","); //$NON-NLS-1$
+			}
+			buf.append(fTaskTags[i].getTag());
+		}
+		String currentTags = buf.toString();
+		if (currentTags.equals(defaultTags) && !fPreferencesLookupOrder[0].getName().equals(DefaultScope.SCOPE)) {
+			if (_debugPreferences) {
+				System.out.println(getClass().getName() + " removing " + TaskTagPreferenceKeys.TASK_TAG_TAGS + " from scope " + fPreferencesLookupOrder[0].getName() + ":" + fPreferencesLookupOrder[0].getLocation()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			}
+			fPreferencesLookupOrder[0].getNode(TaskTagPreferenceKeys.TASK_TAG_NODE).remove(TaskTagPreferenceKeys.TASK_TAG_TAGS);
+		}
+		else {
+			fOwner.requestRedetection();
+			if (_debugPreferences) {
+				System.out.println(getClass().getName() + " setting " + TaskTagPreferenceKeys.TASK_TAG_TAGS + " \"" + currentTags + "\" in scope " + fPreferencesLookupOrder[0].getName() + ":" + fPreferencesLookupOrder[0].getLocation()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			}
+			fPreferencesLookupOrder[0].getNode(TaskTagPreferenceKeys.TASK_TAG_NODE).put(TaskTagPreferenceKeys.TASK_TAG_TAGS, currentTags);
+		}
+
+		StringBuffer buf2 = new StringBuffer();
+		for (int i = 0; i < fTaskTags.length; i++) {
+			if (i > 0) {
+				buf2.append(","); //$NON-NLS-1$
+			}
+			buf2.append(String.valueOf(fTaskTags[i].getPriority()));
+		}
+		String priorities = buf2.toString();
+
+		if (priorities.equals(defaultPriorities) && !fPreferencesLookupOrder[0].getName().equals(DefaultScope.SCOPE)) {
+			if (_debugPreferences) {
+				System.out.println(getClass().getName() + " removing " + TaskTagPreferenceKeys.TASK_TAG_PRIORITIES + " from scope " + fPreferencesLookupOrder[0].getName() + ":" + fPreferencesLookupOrder[0].getLocation()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			}
+			fPreferencesLookupOrder[0].getNode(TaskTagPreferenceKeys.TASK_TAG_NODE).remove(TaskTagPreferenceKeys.TASK_TAG_PRIORITIES);
+		}
+		else {
+			fOwner.requestRedetection();
+			if (_debugPreferences) {
+				System.out.println(getClass().getName() + " setting " + TaskTagPreferenceKeys.TASK_TAG_PRIORITIES + " \"" + priorities + "\" in scope " + fPreferencesLookupOrder[0].getName() + ":" + fPreferencesLookupOrder[0].getLocation()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+			}
+			fPreferencesLookupOrder[0].getNode(TaskTagPreferenceKeys.TASK_TAG_NODE).put(TaskTagPreferenceKeys.TASK_TAG_PRIORITIES, priorities);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.wst.sse.ui/src-tasktags/org/eclipse/wst/sse/ui/internal/preferences/ui/PropertyPreferencePage.java b/bundles/org.eclipse.wst.sse.ui/src-tasktags/org/eclipse/wst/sse/ui/internal/preferences/ui/PropertyPreferencePage.java
new file mode 100644
index 0000000..3aefbf8
--- /dev/null
+++ b/bundles/org.eclipse.wst.sse.ui/src-tasktags/org/eclipse/wst/sse/ui/internal/preferences/ui/PropertyPreferencePage.java
@@ -0,0 +1,305 @@
+/*******************************************************************************
+ * Copyright (c) 2001, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Jens Lukowski/Innoopract - initial renaming/restructuring
+ *     
+ *******************************************************************************/
+package org.eclipse.wst.sse.ui.internal.preferences.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.dialogs.ControlEnableState;
+import org.eclipse.jface.viewers.DecoratingLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.dialogs.ListDialog;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.dialogs.PropertyPage;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.views.navigator.ResourceSorter;
+import org.eclipse.wst.sse.core.internal.tasks.TaskTagPreferenceKeys;
+import org.eclipse.wst.sse.ui.internal.SSEUIMessages;
+import org.eclipse.wst.sse.ui.internal.SSEUIPlugin;
+
+/**
+ * Based loosley on org.eclipse.jdt.internal.ui.preferences.PropertyAndPreferencePage
+ */
+abstract class PropertyPreferencePage extends PropertyPage implements IWorkbenchPreferencePage {
+	private static final boolean _debugPreferences = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.ui/preferences-properties")); //$NON-NLS-1$ //$NON-NLS-2$
+	/*
+	 * Disable link data, prevents the display of a "workspace" or "project"
+	 * settings link to prevent recursive dialog launching
+	 */
+	private static final Object DISABLE_LINK = "DISABLE_LINK"; //$NON-NLS-1$
+
+	private Map fData = null;
+
+	private Button fEnableProjectSettings;
+
+	private Link fProjectSettingsLink;
+
+	private Control fCommon;
+
+	private ControlEnableState fEnablements;
+
+	public PropertyPreferencePage() {
+		super();
+	}
+
+	public final void applyData(Object data) {
+		super.applyData(data);
+		if (data instanceof Map) {
+			fData = (Map) data;
+			updateLinkEnablement();
+		}
+	}
+
+	protected abstract Control createCommonContents(Composite composite);
+
+	public final Control createContents(Composite parent) {
+		Composite composite = new Composite(parent, SWT.NULL);
+
+		GridLayout layout = new GridLayout();
+		composite.setLayout(layout);
+		GridData data = new GridData(GridData.FILL_BOTH);
+		composite.setLayoutData(data);
+
+		Composite checkLinkComposite = new Composite(composite, SWT.NONE);
+		checkLinkComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+		checkLinkComposite.setLayout(new GridLayout(2, false));
+
+		if (getProject() != null) {
+			fEnableProjectSettings = new Button(checkLinkComposite, SWT.CHECK);
+			fEnableProjectSettings.setText(SSEUIMessages.EnableProjectSettings); //$NON-NLS-1$//$NON-NLS-2$
+			fEnableProjectSettings.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
+			boolean enabledForProject = createPreferenceScopes()[0].getNode(getPreferenceNodeQualifier()).getBoolean(getProjectSettingsKey(), false);
+			fEnableProjectSettings.setSelection(enabledForProject);
+		}
+		else {
+			Label spacer = new Label(checkLinkComposite, SWT.CHECK);
+			spacer.setLayoutData(new GridData());
+		}
+
+		fProjectSettingsLink = new Link(checkLinkComposite, SWT.NONE);
+		fProjectSettingsLink.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, true, false));
+
+		/*
+		 * "element" should be a project, if null, link to per-project
+		 * properties
+		 */
+		if (getProject() != null) {
+			fProjectSettingsLink.setText("<a>" + SSEUIMessages.ConfigureWorkspaceSettings + "</a>"); //$NON-NLS-1$//$NON-NLS-2$
+		}
+		else {
+			fProjectSettingsLink.setText("<a>" + SSEUIMessages.ConfigureProjectSettings + "</a>"); //$NON-NLS-1$//$NON-NLS-2$
+		}
+
+		updateLinkEnablement();
+
+		fProjectSettingsLink.addSelectionListener(new SelectionListener() {
+			public void widgetDefaultSelected(SelectionEvent e) {
+				widgetSelected(e);
+			}
+
+			public void widgetSelected(SelectionEvent e) {
+				if (getProject() == null) {
+					openProjectSettings();
+				}
+				else {
+					openWorkspaceSettings();
+				}
+			}
+
+		});
+
+		if (getProject() != null) {
+			Label line = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
+			line.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
+		}
+
+		fCommon = createCommonContents(composite);
+		fCommon.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+		if (fEnableProjectSettings != null) {
+			SelectionAdapter selectionAdapter = new SelectionAdapter() {
+				public void widgetSelected(SelectionEvent e) {
+					super.widgetSelected(e);
+					enablePreferenceContent(fEnableProjectSettings.getSelection());
+				}
+			};
+			selectionAdapter.widgetSelected(null);
+			fEnableProjectSettings.addSelectionListener(selectionAdapter);
+		}
+
+		applyDialogFont(composite);
+		return composite;
+	}
+
+	public void createControl(Composite parent) {
+		if (getProject() != null) {
+			noDefaultAndApplyButton();
+		}
+		super.createControl(parent);
+	}
+
+	protected IScopeContext[] createPreferenceScopes() {
+		IProject project = getProject();
+		if (project != null) {
+			return new IScopeContext[]{new ProjectScope(project), new InstanceScope(), new DefaultScope()};
+		}
+		return new IScopeContext[]{new InstanceScope(), new DefaultScope()};
+	}
+
+	protected abstract String getPreferenceNodeQualifier();
+
+	protected abstract String getPreferencePageID();
+
+	protected IProject getProject() {
+		if (getElement() != null) {
+			if (getElement() instanceof IProject) {
+				return (IProject) getElement();
+			}
+			Object adapter = getElement().getAdapter(IProject.class);
+			if (adapter instanceof IProject) {
+				return (IProject) adapter;
+			}
+			adapter = getElement().getAdapter(IResource.class);
+			if (adapter instanceof IProject) {
+				return (IProject) adapter;
+			}
+		}
+		return null;
+	}
+
+	protected abstract String getProjectSettingsKey();
+
+	protected abstract String getPropertyPageID();
+
+	protected boolean isElementSettingsEnabled() {
+		return fEnableProjectSettings != null && fEnableProjectSettings.getSelection();
+	}
+
+	void openProjectSettings() {
+		ListDialog dialog = new ListDialog(getShell()) {
+
+			protected Control createDialogArea(Composite container) {
+				Control area = super.createDialogArea(container);
+				getTableViewer().setSorter(new ResourceSorter(ResourceSorter.NAME));
+				return area;
+			}
+		};
+		dialog.setMessage(SSEUIMessages.PropertyPreferencePage_02);
+		dialog.setContentProvider(new IStructuredContentProvider() {
+			public void dispose() {
+			}
+
+			public Object[] getElements(Object inputElement) {
+				return ((IWorkspace) inputElement).getRoot().getProjects();
+			}
+
+			public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+			}
+		});
+		dialog.setLabelProvider(new DecoratingLabelProvider(new WorkbenchLabelProvider(), SSEUIPlugin.getDefault().getWorkbench().getDecoratorManager().getLabelDecorator()));
+		dialog.setInput(ResourcesPlugin.getWorkspace());
+		dialog.setTitle(SSEUIMessages.PropertyPreferencePage_01);
+		if (dialog.open() == Window.OK) {
+			Object[] result = dialog.getResult();
+			if (result.length > 0) {
+				IProject project = (IProject) dialog.getResult()[0];
+				Map data = new HashMap();
+				data.put(DISABLE_LINK, Boolean.TRUE);
+				PreferencesUtil.createPropertyDialogOn(getShell(), project, getPropertyPageID(), new String[]{getPropertyPageID()}, data).open();
+			}
+		}
+	}
+
+	void openWorkspaceSettings() {
+		Map data = new HashMap();
+		data.put(DISABLE_LINK, Boolean.TRUE);
+		PreferencesUtil.createPreferenceDialogOn(getShell(), getPreferencePageID(), new String[]{getPreferencePageID()}, data).open();
+	}
+
+	public boolean performOk() {
+		boolean ok = super.performOk();
+		IScopeContext[] preferenceScopes = createPreferenceScopes();
+		if (getProject() != null) {
+			if (isElementSettingsEnabled()) {
+				if (_debugPreferences) {
+					System.out.println(getClass().getName() + " setting " + TaskTagPreferenceKeys.TASK_TAG_PER_PROJECT + " (" + true + ") in scope " + preferenceScopes[0].getName() + ":" + preferenceScopes[0].getLocation()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$  
+				}
+				preferenceScopes[0].getNode(getPreferenceNodeQualifier()).putBoolean(getProjectSettingsKey(), fEnableProjectSettings.getSelection());
+			}
+			else {
+				if (_debugPreferences) {
+					System.out.println(getClass().getName() + " removing " + TaskTagPreferenceKeys.TASK_TAG_PER_PROJECT + " from scope " + preferenceScopes[0].getName() + ":" + preferenceScopes[0].getLocation()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+				}
+				preferenceScopes[0].getNode(getPreferenceNodeQualifier()).remove(getProjectSettingsKey());
+			}
+		}
+		return ok;
+	}
+
+	protected void performDefaults() {
+		if(getProject() != null && fEnableProjectSettings != null) {
+			fEnableProjectSettings.setSelection(false);
+			enablePreferenceContent(false);
+		}
+		super.performDefaults();
+	}
+	
+	/**
+	 * Controls the enablement of the common content region
+	 * of a property or preference page
+	 * 
+	 * @param enable the enabled state of the common content
+	 * area
+	 */
+	protected void enablePreferenceContent(boolean enable) {
+		if(enable) {
+			if(fEnablements != null) {
+				fEnablements.restore();
+				fEnablements = null;
+			}
+		}
+		else {
+			if(fEnablements == null)
+				fEnablements = ControlEnableState.disable(fCommon);
+		}
+	}
+
+	private void updateLinkEnablement() {
+		if (fData != null && fProjectSettingsLink != null) {
+			fProjectSettingsLink.setEnabled(!Boolean.TRUE.equals(fData.get(DISABLE_LINK)));
+		}
+	}
+}
diff --git a/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/reconcile/DocumentRegionProcessor.java b/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/reconcile/DocumentRegionProcessor.java
new file mode 100644
index 0000000..5f4bd9f
--- /dev/null
+++ b/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/reconcile/DocumentRegionProcessor.java
@@ -0,0 +1,442 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     David Carver (Intalio) - bug 307323 - remove extraneous call to spell check strategy
+ *******************************************************************************/
+package org.eclipse.wst.sse.ui.internal.reconcile;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.reconciler.DirtyRegion;
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.projection.ProjectionViewer;
+import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser;
+import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionHandler;
+import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionParser;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.ui.internal.ExtendedConfigurationBuilder;
+import org.eclipse.wst.sse.ui.internal.IReleasable;
+import org.eclipse.wst.sse.ui.internal.Logger;
+import org.eclipse.wst.sse.ui.internal.SSEUIPlugin;
+import org.eclipse.wst.sse.ui.internal.projection.AbstractStructuredFoldingStrategy;
+import org.eclipse.wst.sse.ui.internal.provisional.preferences.CommonEditorPreferenceNames;
+import org.eclipse.wst.sse.ui.internal.reconcile.validator.ValidatorBuilder;
+import org.eclipse.wst.sse.ui.internal.reconcile.validator.ValidatorMetaData;
+import org.eclipse.wst.sse.ui.internal.reconcile.validator.ValidatorStrategy;
+import org.eclipse.wst.sse.ui.internal.spelling.SpellcheckStrategy;
+import org.eclipse.wst.sse.ui.reconcile.ISourceReconcilingListener;
+
+/**
+ * Adds to DirtyRegionProcessor Job: - IDocumentListener - ValidatorStrategy -
+ * Text viewer(dispose, input changed) listeners. - default, spelling, and
+ * validator strategies - DirtyRegion processing logic.
+ */
+public class DocumentRegionProcessor extends DirtyRegionProcessor {
+	private static final boolean DEBUG_VALIDATORS = Boolean.TRUE.toString().equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.ui/debug/reconcilerValidators")); //$NON-NLS-1$
+
+	/**
+	 * Marks the entire document dirty when a parser-level change is notified
+	 * that might affect the entire document
+	 */
+	class DirtyRegionParseHandler implements StructuredDocumentRegionHandler {
+		public void nodeParsed(IStructuredDocumentRegion aCoreStructuredDocumentRegion) {
+		}
+
+		public void resetNodes() {
+			setEntireDocumentDirty(getDocument());
+		}		
+	}
+	private DirtyRegionParseHandler fResetHandler = new DirtyRegionParseHandler();
+	/**
+	 * A strategy to use the defined default Spelling service.
+	 */
+	private IReconcilingStrategy fSpellcheckStrategy;
+
+	/**
+	 * The strategy that runs validators contributed via
+	 * <code>org.eclipse.wst.sse.ui.extensions.sourcevalidation</code>
+	 * extension point
+	 */
+	private ValidatorStrategy fValidatorStrategy;
+
+	private ISourceReconcilingListener[] fReconcileListeners = new ISourceReconcilingListener[0];
+
+	private IReconcilingStrategy fSemanticHighlightingStrategy;
+	
+	/**
+	 * The folding strategy for this processor
+	 */
+	private AbstractStructuredFoldingStrategy fFoldingStrategy;
+	
+	private final String SSE_UI_ID = "org.eclipse.wst.sse.ui"; //$NON-NLS-1$
+
+	/**
+	 * true if as you type validation is enabled,
+	 * false otherwise
+	 */
+	private boolean fValidationEnabled;
+
+	public void addReconcilingListener(ISourceReconcilingListener listener) {
+		Set listeners = new HashSet(Arrays.asList(fReconcileListeners));
+		listeners.add(listener);
+		fReconcileListeners = (ISourceReconcilingListener[]) listeners.toArray(new ISourceReconcilingListener[listeners.size()]);
+	}
+
+	protected void beginProcessing() {
+		super.beginProcessing();
+		ValidatorStrategy validatorStrategy = getValidatorStrategy();
+		if (validatorStrategy != null) {
+			validatorStrategy.beginProcessing();
+		}
+		if ((getTextViewer() instanceof ISourceViewer)) {
+			for (int i = 0; i < fReconcileListeners.length; i++) {
+				fReconcileListeners[i].aboutToBeReconciled();
+			}
+		}
+	}
+
+	protected void endProcessing() {
+		super.endProcessing();
+		ValidatorStrategy validatorStrategy = getValidatorStrategy();
+		if (validatorStrategy != null) {
+			validatorStrategy.endProcessing();
+		}
+		/* single spell-check for everything to ensure that SpellingProblem offsets are correct */
+		IReconcilingStrategy spellingStrategy = getSpellcheckStrategy();
+		IDocument document = getDocument();
+		if (spellingStrategy != null && document != null) {
+			 spellingStrategy.reconcile(new Region(0, document.getLength()));
+		}
+		
+		IReconcilingStrategy semanticHighlightingStrategy = getSemanticHighlightingStrategy();
+		if (semanticHighlightingStrategy != null && document != null) {
+			semanticHighlightingStrategy.reconcile(new Region(0, document.getLength()));
+		}
+
+		if ((getTextViewer() instanceof ISourceViewer)) {
+			ISourceViewer sourceViewer = (ISourceViewer) getTextViewer();
+			IAnnotationModel annotationModel = sourceViewer.getAnnotationModel();
+			for (int i = 0; i < fReconcileListeners.length; i++) {
+				fReconcileListeners[i].reconciled(document, annotationModel, false, new NullProgressMonitor());
+			}
+		}
+	}
+
+	public void forceReconciling() {
+		super.forceReconciling();
+	}
+
+	protected String getContentType(IDocument doc) {
+		if (doc == null)
+			return null;
+
+		String contentTypeId = null;
+
+		IContentType ct = null;
+		try {
+			IContentDescription desc = Platform.getContentTypeManager().getDescriptionFor(new StringReader(doc.get()), null, IContentDescription.ALL);
+			if (desc != null) {
+				ct = desc.getContentType();
+				if (ct != null)
+					contentTypeId = ct.getId();
+			}
+		}
+		catch (IOException e) {
+			// just bail
+		}
+		return contentTypeId;
+	}
+
+	protected IReconcilingStrategy getSpellcheckStrategy() {
+		if (fSpellcheckStrategy == null && getDocument() != null) {
+			String contentTypeId = getContentType(getDocument());
+			if (contentTypeId == null) {
+				contentTypeId = IContentTypeManager.CT_TEXT;
+			}
+			if (getTextViewer() instanceof ISourceViewer) {
+				ISourceViewer viewer = (ISourceViewer) getTextViewer();
+				fSpellcheckStrategy = new SpellcheckStrategy(viewer, contentTypeId);
+				fSpellcheckStrategy.setDocument(getDocument());
+			}
+		}
+		return fSpellcheckStrategy;
+	}
+	
+	/**
+	 * <p>Get the folding strategy for this processor. Retrieved from the 
+	 * extended configuration builder.  The processor chosen is set by the plugin.</p>
+	 * 
+	 * <p>EX:<br />
+	 * <code>&lt;extension point="org.eclipse.wst.sse.ui.editorConfiguration"&gt;<br />
+	 *  &lt;provisionalConfiguration<br />
+	 *			type="foldingstrategy"<br />
+	 *			class="org.eclipse.wst.xml.ui.internal.projection.XMLFoldingStrategy"<br />
+	 *			target="org.eclipse.core.runtime.xml, org.eclipse.wst.xml.core.xmlsource" /&gt;<br />
+	 *	&lt;/extension&gt;</code></p>
+	 * 
+	 * <p>The type must be equal to <code>AbstractFoldingStrategy.ID</code> (AKA: foldingstrategy)
+	 * and the class must extend <code>org.eclipse.wst.sse.ui.internal.projection.AbstractFoldingStrategy</code>
+	 * and the target must be a structured editor content type ID</p>
+	 * 
+	 * @return the requested folding strategy or null if none can be found
+	 */
+	protected IReconcilingStrategy getFoldingStrategy() {
+		if(fFoldingStrategy == null && getDocument() != null) {
+			String contentTypeId = getContentType(getDocument());
+			if (contentTypeId == null) {
+				contentTypeId = IContentTypeManager.CT_TEXT;
+			}
+			
+			ITextViewer viewer = getTextViewer();
+			if(viewer instanceof ProjectionViewer) {
+				ExtendedConfigurationBuilder builder = ExtendedConfigurationBuilder.getInstance();
+				
+				IContentType type = Platform.getContentTypeManager().getContentType(contentTypeId);
+				while(fFoldingStrategy == null && type != null) {
+					fFoldingStrategy =(AbstractStructuredFoldingStrategy) builder.getConfiguration(
+							AbstractStructuredFoldingStrategy.ID, type.getId());
+				
+					type = type.getBaseType();
+				}
+				
+				if(fFoldingStrategy != null) {
+					fFoldingStrategy.setViewer((ProjectionViewer)viewer);
+					fFoldingStrategy.setDocument(getDocument());
+				}
+			}
+		}
+		
+		return fFoldingStrategy;
+	}
+	
+	/**
+	 * Enable or disable as you type validation. Typically set by a user preference
+	 * 
+	 * @param enable true to enable as you type validation, false to disable
+	 */
+	public void setValidatorStrategyEnabled(boolean enable) {
+		fValidationEnabled = enable;
+	}
+
+	/**
+	 * @return Returns the ValidatorStrategy.
+	 */
+	protected ValidatorStrategy getValidatorStrategy() {
+		ValidatorStrategy validatorStrategy = null;
+		if (fValidatorStrategy == null && fValidationEnabled) {
+			if (getTextViewer() instanceof ISourceViewer) {
+				ISourceViewer viewer = (ISourceViewer) getTextViewer();
+				String contentTypeId = null;
+
+				IDocument doc = viewer.getDocument();
+				contentTypeId = getContentType(doc);
+
+				if (contentTypeId != null) {
+					validatorStrategy = new ValidatorStrategy(viewer, contentTypeId);
+					ValidatorBuilder vBuilder = new ValidatorBuilder();
+					ValidatorMetaData[] vmds = vBuilder.getValidatorMetaData(SSE_UI_ID);
+					List enabledValidators = new ArrayList(1);
+					/* if any "must" handle this content type, just add them */
+					boolean foundSpecificContentTypeValidators = false;
+					for (int i = 0; i < vmds.length; i++) {
+						if (vmds[i].mustHandleContentType(contentTypeId)) {
+							if (DEBUG_VALIDATORS)
+								Logger.log(Logger.INFO, contentTypeId + " using specific validator " + vmds[i].getValidatorId()); //$NON-NLS-1$
+							foundSpecificContentTypeValidators = true;
+							enabledValidators.add(vmds[i]);
+						}
+					}
+					if (!foundSpecificContentTypeValidators) {
+						for (int i = 0; i < vmds.length; i++) {
+							if (vmds[i].canHandleContentType(contentTypeId)) {
+								if (DEBUG_VALIDATORS)
+									Logger.log(Logger.INFO, contentTypeId + " using inherited(?) validator " + vmds[i].getValidatorId()); //$NON-NLS-1$
+								enabledValidators.add(vmds[i]);
+							}
+						}
+					}
+					for (int i = 0; i < enabledValidators.size(); i++) {
+						validatorStrategy.addValidatorMetaData((ValidatorMetaData) enabledValidators.get(i));
+					}
+				}
+			}
+			fValidatorStrategy = validatorStrategy;
+		} else if(fValidatorStrategy != null && fValidationEnabled) {
+			validatorStrategy = fValidatorStrategy;
+		}
+		return validatorStrategy;
+	}
+	
+	public void setSemanticHighlightingStrategy(IReconcilingStrategy semanticHighlightingStrategy) {
+		fSemanticHighlightingStrategy = semanticHighlightingStrategy;
+		fSemanticHighlightingStrategy.setDocument(getDocument());
+	}
+	
+	protected IReconcilingStrategy getSemanticHighlightingStrategy() {
+		return fSemanticHighlightingStrategy;
+	}
+	
+	/**
+	 * @param dirtyRegion
+	 */
+	protected void process(DirtyRegion dirtyRegion) {
+		if (!isInstalled() || isInRewriteSession() || dirtyRegion == null || getDocument() == null)
+			return;
+
+		super.process(dirtyRegion);
+		
+		ITypedRegion[] partitions = computePartitioning(dirtyRegion);
+		
+		// call the validator strategy once for each effected partition
+		DirtyRegion dirty = null;
+		for (int i = 0; i < partitions.length; i++) {
+			dirty = createDirtyRegion(partitions[i], DirtyRegion.INSERT);
+
+			// [source]validator (extension) for this partition
+			if (getValidatorStrategy() != null) {
+				getValidatorStrategy().reconcile(partitions[i], dirty);
+			}
+		}
+		
+		/* if there is a folding strategy then reconcile it for the
+		 * entire dirty region.
+		 * NOTE: the folding strategy does not care about the sub regions.
+		 */
+		if(getFoldingStrategy() != null) {
+			getFoldingStrategy().reconcile(dirtyRegion, null);
+		}
+	}
+	
+	public void removeReconcilingListener(ISourceReconcilingListener listener) {
+		Set listeners = new HashSet(Arrays.asList(fReconcileListeners));
+		listeners.remove(listener);
+		fReconcileListeners = (ISourceReconcilingListener[]) listeners.toArray(new ISourceReconcilingListener[listeners.size()]);
+	}
+
+
+	public void setDocument(IDocument doc) {
+		if (getDocument() instanceof IStructuredDocument) {
+			RegionParser parser = ((IStructuredDocument) getDocument()).getParser();
+			if (parser instanceof StructuredDocumentRegionParser) {
+				((StructuredDocumentRegionParser) parser).removeStructuredDocumentRegionHandler(fResetHandler);
+			}
+		}
+		
+		super.setDocument(doc);
+		
+		IReconcilingStrategy validatorStrategy = getValidatorStrategy();
+		if (validatorStrategy != null) {
+			validatorStrategy.setDocument(doc);
+		}
+		if (fSemanticHighlightingStrategy != null) {
+			fSemanticHighlightingStrategy.setDocument(doc);
+		}
+		
+		fSpellcheckStrategy = null;
+		if(fFoldingStrategy != null) {
+			fFoldingStrategy.uninstall();
+		}
+		fFoldingStrategy = null;
+		
+		if (getDocument() instanceof IStructuredDocument) {
+			RegionParser parser = ((IStructuredDocument) doc).getParser();
+			if (parser instanceof StructuredDocumentRegionParser) {
+				((StructuredDocumentRegionParser) parser).addStructuredDocumentRegionHandler(fResetHandler);
+			}
+		}
+	}
+
+	protected void setEntireDocumentDirty(IDocument document) {
+		super.setEntireDocumentDirty(document);
+
+		// make the entire document dirty
+		// this also happens on a "save as"
+		if (document != null && isInstalled() && fLastPartitions != null && document.getLength() == 0) {
+			/**
+			 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=199053
+			 * 
+			 * Process the strategies for the last known-good partitions.
+			 */
+			for (int i = 0; i < fLastPartitions.length; i++) {
+				ValidatorStrategy validatorStrategy = getValidatorStrategy();
+				if (validatorStrategy != null) {
+					validatorStrategy.reconcile(fLastPartitions[i], createDirtyRegion(fLastPartitions[i], DirtyRegion.REMOVE));
+				}
+			}
+			IReconcilingStrategy spellingStrategy = getSpellcheckStrategy();
+			if (spellingStrategy != null) {
+				spellingStrategy.reconcile(new Region(0, document.getLength()));
+			}
+			
+			//if there is a folding strategy then reconcile it
+			if(getFoldingStrategy() != null) {
+				getFoldingStrategy().reconcile(new Region(0, document.getLength()));
+			}
+		}
+	}
+
+	/**
+	 * @see org.eclipse.wst.sse.ui.internal.reconcile.DirtyRegionProcessor#install(org.eclipse.jface.text.ITextViewer)
+	 */
+	public void install(ITextViewer textViewer) {
+		super.install(textViewer);
+
+		//determine if validation is enabled
+		this.fValidationEnabled = SSEUIPlugin.getInstance().getPreferenceStore().getBoolean(
+				CommonEditorPreferenceNames.EVALUATE_TEMPORARY_PROBLEMS);
+	}
+	
+	/**
+	 * @see org.eclipse.wst.sse.ui.internal.reconcile.DirtyRegionProcessor#uninstall()
+	 */
+	public void uninstall() {
+		if (isInstalled()) {
+
+			IReconcilingStrategy validatorStrategy = getValidatorStrategy();
+
+			if (validatorStrategy != null) {
+				if (validatorStrategy instanceof IReleasable)
+					((IReleasable) validatorStrategy).release();
+			}
+			
+			if (fSpellcheckStrategy != null) {
+				fSpellcheckStrategy.setDocument(null);
+				fSpellcheckStrategy = null;
+			}
+
+			fReconcileListeners = new ISourceReconcilingListener[0];
+
+			if (getDocument() instanceof IStructuredDocument) {
+				RegionParser parser = ((IStructuredDocument) getDocument()).getParser();
+				if (parser instanceof StructuredDocumentRegionParser) {
+					((StructuredDocumentRegionParser) parser).removeStructuredDocumentRegionHandler(fResetHandler);
+				}
+			}
+		}
+		super.uninstall();
+	}
+}
diff --git a/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/style/SemanticHighlightingPresenter.java b/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/style/SemanticHighlightingPresenter.java
new file mode 100644
index 0000000..093ec05
--- /dev/null
+++ b/bundles/org.eclipse.wst.sse.ui/src/org/eclipse/wst/sse/ui/internal/style/SemanticHighlightingPresenter.java
@@ -0,0 +1,1067 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.sse.ui.internal.style;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ISynchronizable;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextPresentationListener;
+import org.eclipse.jface.text.ITextViewerExtension2;
+import org.eclipse.jface.text.ITextViewerExtension4;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.ui.internal.Logger;
+import org.eclipse.wst.sse.ui.internal.SSEUIPlugin;
+import org.eclipse.wst.sse.ui.internal.StructuredTextViewer;
+import org.eclipse.wst.sse.ui.internal.preferences.EditorPreferenceNames;
+import org.eclipse.wst.sse.ui.internal.provisional.style.StructuredPresentationReconciler;
+import org.eclipse.wst.sse.ui.internal.style.SemanticHighlightingManager.HighlightedPosition;
+import org.eclipse.wst.sse.ui.internal.style.SemanticHighlightingManager.HighlightingStyle;
+import org.eclipse.wst.sse.ui.internal.util.EditorUtility;
+
+/**
+ * Semantic highlighting presenter - UI thread implementation.  Based on 
+ * org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightingPresenter
+ *
+ * @since 3.1
+ */
+public class SemanticHighlightingPresenter implements ITextPresentationListener, ITextInputListener, IDocumentListener {
+
+	/**
+	 * Semantic highlighting position updater.
+	 */
+	private class HighlightingPositionUpdater implements IPositionUpdater {
+
+		/** The position category. */
+		private final String fCategory;
+
+		/**
+		 * Creates a new updater for the given <code>category</code>.
+		 *
+		 * @param category the new category.
+		 */
+		public HighlightingPositionUpdater(String category) {
+			fCategory= category;
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
+		 */
+		public void update(DocumentEvent event) {
+
+			int eventOffset= event.getOffset();
+			int eventOldLength= event.getLength();
+			int eventEnd= eventOffset + eventOldLength;
+
+			try {
+				Position[] positions= event.getDocument().getPositions(fCategory);
+
+				for (int i= 0; i != positions.length; i++) {
+
+					HighlightedPosition position= (HighlightedPosition) positions[i];
+
+					// Also update deleted positions because they get deleted by the background thread and removed/invalidated only in the UI runnable
+//					if (position.isDeleted())
+//						continue;
+
+					int offset= position.getOffset();
+					int length= position.getLength();
+					int end= offset + length;
+
+					if (offset > eventEnd)
+						updateWithPrecedingEvent(position, event);
+					else if (end < eventOffset) {
+						// do nothing
+						// updateWithSucceedingEvent(position, event);
+					}
+					else if (offset <= eventOffset && end >= eventEnd) {
+						 // Previous region updated to overlap the beginning of this one; just bump the start.
+						if (i > 0 && positions[i - 1].offset + positions[i - 1].length > offset)
+							updateWithPrecedingEvent(position, event);
+						else
+							updateWithIncludedEvent(position, event);
+					}
+					else if (offset <= eventOffset)
+						updateWithOverEndEvent(position, event);
+					else if (end >= eventEnd)
+						updateWithOverStartEvent(position, event);
+					else
+						updateWithIncludingEvent(position, event);
+				}
+			} catch (BadPositionCategoryException e) {
+				// ignore and return
+			}
+		}
+
+		/**
+		 * Update the given position with the given event. The event precedes the position.
+		 *
+		 * @param position The position
+		 * @param event The event
+		 */
+		private void updateWithPrecedingEvent(HighlightedPosition position, DocumentEvent event) {
+			String newText= event.getText();
+			int eventNewLength= newText != null ? newText.length() : 0;
+			int deltaLength= eventNewLength - event.getLength();
+
+			position.setOffset(position.getOffset() + deltaLength);
+		}
+
+		/**
+		 * Update the given position with the given event. The event succeeds the position.
+		 *
+		 * @param position The position
+		 * @param event The event
+		 */
+//		private void updateWithSucceedingEvent(HighlightedPosition position, DocumentEvent event) {
+//		}
+
+		/**
+		 * Update the given position with the given event. The event is included by the position.
+		 *
+		 * @param position The position
+		 * @param event The event
+		 */
+		private void updateWithIncludedEvent(HighlightedPosition position, DocumentEvent event) {
+			int eventOffset= event.getOffset();
+			String newText= event.getText();
+			if (newText == null)
+				newText= ""; //$NON-NLS-1$
+			int eventNewLength= newText.length();
+
+			int deltaLength= eventNewLength - event.getLength();
+
+			int offset= position.getOffset();
+			int length= position.getLength();
+			int end= offset + length;
+
+			int includedLength= 0;
+			while (includedLength < eventNewLength && !Character.isWhitespace(newText.charAt(includedLength)))
+				includedLength++;
+			if (includedLength == eventNewLength)
+				position.setLength(length + deltaLength);
+			else {
+				int newLeftLength= eventOffset - offset + includedLength;
+
+				int excludedLength= eventNewLength;
+				while (excludedLength > 0 && !Character.isWhitespace(newText.charAt(excludedLength - 1)))
+					excludedLength--;
+				int newRightOffset= eventOffset + excludedLength;
+				int newRightLength= end + deltaLength - newRightOffset;
+
+				if (newRightLength == 0) {
+					position.setLength(newLeftLength);
+				} else {
+					if (newLeftLength == 0) {
+						position.update(newRightOffset, newRightLength);
+					} else {
+						position.setLength(newLeftLength);
+						addPositionFromUI(position);
+					}
+				}
+			}
+		}
+
+		/**
+		 * Update the given position with the given event. The event overlaps with the end of the position.
+		 *
+		 * @param position The position
+		 * @param event The event
+		 */
+		private void updateWithOverEndEvent(HighlightedPosition position, DocumentEvent event) {
+			String newText= event.getText();
+			if (newText == null)
+				newText= ""; //$NON-NLS-1$
+			int eventNewLength= newText.length();
+
+			int includedLength= 0;
+			while (includedLength < eventNewLength && !Character.isWhitespace(newText.charAt(includedLength)))
+				includedLength++;
+			position.setLength(event.getOffset() - position.getOffset() + includedLength);
+		}
+
+		/**
+		 * Update the given position with the given event. The event overlaps with the start of the position.
+		 *
+		 * @param position The position
+		 * @param event The event
+		 */
+		private void updateWithOverStartEvent(HighlightedPosition position, DocumentEvent event) {
+			int eventOffset= event.getOffset();
+			int eventEnd= eventOffset + event.getLength();
+
+			String newText= event.getText();
+			if (newText == null)
+				newText= ""; //$NON-NLS-1$
+			int eventNewLength= newText.length();
+
+			int excludedLength= eventNewLength;
+			while (excludedLength > 0 && !Character.isWhitespace(newText.charAt(excludedLength - 1)))
+				excludedLength--;
+			int deleted= eventEnd - position.getOffset();
+			int inserted= eventNewLength - excludedLength;
+			position.update(eventOffset + excludedLength, position.getLength() - deleted + inserted);
+		}
+
+		/**
+		 * Update the given position with the given event. The event includes the position.
+		 *
+		 * @param position The position
+		 * @param event The event
+		 */
+		private void updateWithIncludingEvent(HighlightedPosition position, DocumentEvent event) {
+			position.delete();
+			position.update(event.getOffset(), 0);
+		}
+	}
+
+	/** Position updater */
+	private IPositionUpdater fPositionUpdater= new HighlightingPositionUpdater(getPositionCategory());
+
+	/** The source viewer this semantic highlighting reconciler is installed on */
+	private ISourceViewer fSourceViewer;
+	/** The background presentation reconciler */
+	private StructuredPresentationReconciler fPresentationReconciler;
+
+	/** UI's current highlighted positions - can contain <code>null</code> elements */
+	private List fPositions= new ArrayList();
+	/** UI position lock */
+	private Object fPositionLock= new Object();
+
+	/** <code>true</code> iff the current reconcile is canceled. */
+	private boolean fIsCanceled= false;
+
+	/** Read-only color support */
+	private YUV_RGBConverter rgbConverter;
+	private Map readOnlyColorTable;
+	double readOnlyForegroundScaleFactor = 30;
+	
+	public SemanticHighlightingPresenter() {
+		// no listener for now since there's no UI to change the value
+		IPreferenceStore editorStore = SSEUIPlugin.getDefault().getPreferenceStore();
+		readOnlyForegroundScaleFactor = editorStore.getInt(EditorPreferenceNames.READ_ONLY_FOREGROUND_SCALE);
+	}
+
+	/**
+	 * Creates and returns a new highlighted position with the given offset, length and highlighting.
+	 * <p>
+	 * NOTE: Also called from background thread.
+	 * </p>
+	 *
+	 * @param offset The offset
+	 * @param length The length
+	 * @param highlighting The highlighting
+	 * @return The new highlighted position
+	 */
+	public HighlightedPosition createHighlightedPosition(int offset, int length, HighlightingStyle highlighting) {
+		// TODO: reuse deleted positions
+		return new HighlightedPosition(offset, length, highlighting, fPositionUpdater);
+	}
+	
+	/**
+	 * Creates and returns a new highlighted position from the given position and highlighting.
+	 * <p>
+	 * NOTE: Also called from background thread.
+	 * </p>
+	 *
+	 * @param position The position
+	 * @param highlighting The highlighting
+	 * @return The new highlighted position
+	 */
+	public HighlightedPosition createHighlightedPosition(Position position, HighlightingStyle highlighting) {
+		// TODO: reuse deleted positions
+		return new HighlightedPosition(position, highlighting, fPositionUpdater);
+	}
+
+	/**
+	 * Creates and returns a new highlighted position from the given position and highlighting.
+	 * <p>
+	 * NOTE: Also called from background thread.
+	 * </p>
+	 *
+	 * @param position The position
+	 * @param highlighting The highlighting
+	 * @param isReadOnly Is this a read-only position
+	 * @return The new highlighted position
+	 */
+	public HighlightedPosition createHighlightedPosition(Position position, HighlightingStyle highlighting, boolean isReadOnly) {
+		// TODO: reuse deleted positions
+		return new HighlightedPosition(position, highlighting, fPositionUpdater, isReadOnly);
+	}
+
+	/**
+	 * Adds all current positions to the given list.
+	 * <p>
+	 * NOTE: Called from background thread.
+	 * </p>
+	 *
+	 * @param list The list
+	 */
+	public void addAllPositions(List list) {
+		synchronized (fPositionLock) {
+			list.addAll(fPositions);
+		}
+	}
+
+	/**
+	 * Create a text presentation in the background.
+	 * <p>
+	 * NOTE: Called from background thread.
+	 * </p>
+	 *
+	 * @param addedPositions the added positions
+	 * @param removedPositions the removed positions
+	 * @return the text presentation or <code>null</code>, if reconciliation should be canceled
+	 */
+	public TextPresentation createPresentation(List addedPositions, List removedPositions) {
+		ISourceViewer sourceViewer= fSourceViewer;
+		StructuredPresentationReconciler presentationReconciler= fPresentationReconciler;
+		if (sourceViewer == null || presentationReconciler == null)
+			return null;
+
+		if (isCanceled())
+			return null;
+
+		IDocument document= sourceViewer.getDocument();
+		if (document == null)
+			return null;
+
+		int minStart= Integer.MAX_VALUE;
+		int maxEnd= Integer.MIN_VALUE;
+		for (int i= 0, n= removedPositions.size(); i < n; i++) {
+			Position position= (Position) removedPositions.get(i);
+			int offset= position.getOffset();
+			minStart= Math.min(minStart, offset);
+			maxEnd= Math.max(maxEnd, offset + position.getLength());
+		}
+		for (int i= 0, n= addedPositions.size(); i < n; i++) {
+			Position position= (Position) addedPositions.get(i);
+			int offset= position.getOffset();
+			minStart= Math.min(minStart, offset);
+			maxEnd= Math.max(maxEnd, offset + position.getLength());
+		}
+
+		if (minStart < maxEnd)
+			try {
+				return presentationReconciler.createRepairDescription(new Region(minStart, maxEnd - minStart), document);
+			} catch (RuntimeException e) {
+				// Assume concurrent modification from UI thread
+			}
+
+		return null;
+	}
+
+	/**
+	 * Create a runnable for updating the presentation.
+	 * <p>
+	 * NOTE: Called from background thread.
+	 * </p>
+	 * @param textPresentation the text presentation
+	 * @param addedPositions the added positions
+	 * @param removedPositions the removed positions
+	 * @return the runnable or <code>null</code>, if reconciliation should be canceled
+	 */
+	public Runnable createUpdateRunnable(final TextPresentation textPresentation, List addedPositions, List removedPositions) {
+		if (fSourceViewer == null || textPresentation == null)
+			return null;
+
+		// TODO: do clustering of positions and post multiple fast runnables
+		final HighlightedPosition[] added= new SemanticHighlightingManager.HighlightedPosition[addedPositions.size()];
+		addedPositions.toArray(added);
+		final SemanticHighlightingManager.HighlightedPosition[] removed= new SemanticHighlightingManager.HighlightedPosition[removedPositions.size()];
+		removedPositions.toArray(removed);
+
+		if (isCanceled())
+			return null;
+
+		Runnable runnable= new Runnable() {
+			public void run() {
+				updatePresentation(textPresentation, added, removed);
+			}
+		};
+		return runnable;
+	}
+
+	/**
+	 * Invalidate the presentation of the positions based on the given added positions and the existing deleted positions.
+	 * Also unregisters the deleted positions from the document and patches the positions of this presenter.
+	 * <p>
+	 * NOTE: Indirectly called from background thread by UI runnable.
+	 * </p>
+	 * @param textPresentation the text presentation or <code>null</code>, if the presentation should computed in the UI thread
+	 * @param addedPositions the added positions
+	 * @param removedPositions the removed positions
+	 */
+	public void updatePresentation(TextPresentation textPresentation, HighlightedPosition[] addedPositions, HighlightedPosition[] removedPositions) {
+		if (fSourceViewer == null)
+			return;
+
+//		checkOrdering("added positions: ", Arrays.asList(addedPositions)); //$NON-NLS-1$
+//		checkOrdering("removed positions: ", Arrays.asList(removedPositions)); //$NON-NLS-1$
+//		checkOrdering("old positions: ", fPositions); //$NON-NLS-1$
+
+		// TODO: double-check consistency with document.getPositions(...)
+		// TODO: reuse removed positions
+		if (isCanceled())
+			return;
+
+		IDocument document= fSourceViewer.getDocument();
+		if (document == null)
+			return;
+
+		String positionCategory= getPositionCategory();
+
+		List removedPositionsList= Arrays.asList(removedPositions);
+
+		try {
+			synchronized (fPositionLock) {
+				List oldPositions= fPositions;
+				int newSize= Math.max(fPositions.size() + addedPositions.length - removedPositions.length, 10);
+
+				/*
+				 * The following loop is a kind of merge sort: it merges two List<Position>, each
+				 * sorted by position.offset, into one new list. The first of the two is the
+				 * previous list of positions (oldPositions), from which any deleted positions get
+				 * removed on the fly. The second of two is the list of added positions. The result
+				 * is stored in newPositions.
+				 */
+				List newPositions= new ArrayList(newSize);
+				Position position= null;
+				Position addedPosition= null;
+				for (int i= 0, j= 0, n= oldPositions.size(), m= addedPositions.length; i < n || position != null || j < m || addedPosition != null;) {
+					// loop variant: i + j < old(i + j)
+
+					// a) find the next non-deleted Position from the old list
+					while (position == null && i < n) {
+						position= (Position) oldPositions.get(i++);
+						if (position.isDeleted() || contain(removedPositionsList, position)) {
+							document.removePosition(positionCategory, position);
+							position= null;
+						}
+					}
+
+					// b) find the next Position from the added list
+					if (addedPosition == null && j < m) {
+						addedPosition= addedPositions[j++];
+						document.addPosition(positionCategory, addedPosition);
+					}
+
+					// c) merge: add the next of position/addedPosition with the lower offset
+					if (position != null) {
+						if (addedPosition != null)
+							if (position.getOffset() <= addedPosition.getOffset()) {
+								newPositions.add(position);
+								position= null;
+							} else {
+								newPositions.add(addedPosition);
+								addedPosition= null;
+							}
+						else {
+							newPositions.add(position);
+							position= null;
+						}
+					} else if (addedPosition != null) {
+						newPositions.add(addedPosition);
+						addedPosition= null;
+					}
+				}
+				fPositions= newPositions;
+				Collections.sort(fPositions, new Comparator() {
+					
+					public int compare(Object arg0, Object arg1) {
+						Position p1 = (Position) arg0;
+						Position p2 = (Position) arg1;
+						return p1.offset - p2.offset;
+					}
+				});
+			}
+		} catch (BadPositionCategoryException e) {
+			// Should not happen
+			Logger.logException(e);
+		} catch (BadLocationException e) {
+			// Should not happen
+			Logger.logException(e);
+		}
+//		checkOrdering("new positions: ", fPositions); //$NON-NLS-1$
+
+		if (textPresentation != null)
+			fSourceViewer.changeTextPresentation(textPresentation, false);
+		else
+			fSourceViewer.invalidateTextPresentation();
+	}
+
+//	private void checkOrdering(String s, List positions) {
+//		Position previous= null;
+//		for (int i= 0, n= positions.size(); i < n; i++) {
+//			Position current= (Position) positions.get(i);
+//			if (previous != null && previous.getOffset() + previous.getLength() > current.getOffset())
+//				return;
+//		}
+//	}
+
+	/**
+	 * Returns <code>true</code> iff the positions contain the position.
+	 * @param positions the positions, must be ordered by offset but may overlap
+	 * @param position the position
+	 * @return <code>true</code> iff the positions contain the position
+	 */
+	private boolean contain(List positions, Position position) {
+		return indexOf(positions, position) != -1;
+	}
+
+	/**
+	 * Returns index of the position in the positions, <code>-1</code> if not found.
+	 * @param positions the positions, must be ordered by offset but may overlap
+	 * @param position the position
+	 * @return the index
+	 */
+	private int indexOf(List positions, Position position) {
+		int index= computeIndexAtOffset(positions, position.getOffset());
+		int size= positions.size();
+		while (index < size) {
+			if (positions.get(index) == position)
+				return index;
+			index++;
+		}
+		return -1;
+	}
+
+	/**
+	 * Insert the given position in <code>fPositions</code>, s.t. the offsets remain in linear order.
+	 *
+	 * @param position The position for insertion
+	 */
+	private void insertPosition(Position position) {
+		int i= computeIndexAfterOffset(fPositions, position.getOffset());
+		fPositions.add(i, position);
+	}
+
+	/**
+	 * Returns the index of the first position with an offset greater than the given offset.
+	 *
+	 * @param positions the positions, must be ordered by offset and must not overlap
+	 * @param offset the offset
+	 * @return the index of the last position with an offset greater than the given offset
+	 */
+	private int computeIndexAfterOffset(List positions, int offset) {
+		int i= -1;
+		int j= positions.size();
+		while (j - i > 1) {
+			int k= (i + j) >> 1;
+			Position position= (Position) positions.get(k);
+			if (position.getOffset() > offset)
+				j= k;
+			else
+				i= k;
+		}
+		return j;
+	}
+
+	/**
+	 * Returns the index of the first position with an offset equal or greater than the given offset.
+	 *
+	 * @param positions the positions, must be ordered by offset and must not overlap
+	 * @param offset the offset
+	 * @return the index of the last position with an offset equal or greater than the given offset
+	 */
+	private int computeIndexAtOffset(List positions, int offset) {
+		int i= -1;
+		int j= positions.size();
+		while (j - i > 1) {
+			int k= (i + j) >> 1;
+			Position position= (Position) positions.get(k);
+			if (position.getOffset() >= offset)
+				j= k;
+			else
+				i= k;
+		}
+		return j;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation)
+	 */
+	public void applyTextPresentation(TextPresentation textPresentation) {
+		IRegion region= textPresentation.getExtent();
+		int minStart= Integer.MAX_VALUE;
+		int maxEnd= Integer.MIN_VALUE;
+		int i= computeIndexAtOffset(fPositions, region.getOffset()), n= computeIndexAtOffset(fPositions, region.getOffset() + region.getLength());
+		if (n - i > 2) {
+			List ranges= new ArrayList(n - i);
+			for (; i < n; i++) {
+				HighlightedPosition position= (HighlightedPosition) fPositions.get(i);
+				if (!position.isDeleted()) {
+					if (!position.isReadOnly())
+						ranges.add(position.createStyleRange());
+					else {
+						int offset= position.getOffset();
+						minStart= Math.min(minStart, offset);
+						maxEnd= Math.max(maxEnd, offset + position.getLength());
+					}
+						
+				}
+			}
+			StyleRange[] array= new StyleRange[ranges.size()];
+			array= (StyleRange[]) ranges.toArray(array);
+			textPresentation.replaceStyleRanges(array);
+		} else {
+			for (; i < n; i++) {
+				HighlightedPosition position= (HighlightedPosition) fPositions.get(i);
+				if (!position.isDeleted()) {
+					if (!position.isReadOnly())
+						textPresentation.replaceStyleRange(position.createStyleRange());
+					else {
+						int offset= position.getOffset();
+						minStart= Math.min(minStart, offset);
+						maxEnd= Math.max(maxEnd, offset + position.getLength());
+					}
+				}
+			}
+		}
+		if (minStart < maxEnd) {
+			IStructuredDocument document = (IStructuredDocument) fSourceViewer.getDocument();
+			if (document.containsReadOnly(minStart, maxEnd)) {
+				Iterator nonDefaultStyleRangeIterator = textPresentation.getNonDefaultStyleRangeIterator();
+				while (nonDefaultStyleRangeIterator.hasNext()) {
+					StyleRange styleRange = (StyleRange) nonDefaultStyleRangeIterator.next();
+					if (document.containsReadOnly(styleRange.start, styleRange.length)) {
+						adjustForeground(styleRange);
+					}
+				}
+			}
+		}
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+	 */
+	public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+		setCanceled(true);
+		releaseDocument(oldInput);
+		resetState();
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
+	 */
+	public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+		manageDocument(newInput);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+	 */
+	public void documentAboutToBeChanged(DocumentEvent event) {
+		setCanceled(true);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
+	 */
+	public void documentChanged(DocumentEvent event) {
+	}
+
+	/**
+	 * @return Returns <code>true</code> iff the current reconcile is canceled.
+	 * <p>
+	 * NOTE: Also called from background thread.
+	 * </p>
+	 */
+	public boolean isCanceled() {
+		IDocument document= fSourceViewer != null ? fSourceViewer.getDocument() : null;
+		if (document == null)
+			return fIsCanceled;
+
+		synchronized (getLockObject(document)) {
+			return fIsCanceled;
+		}
+	}
+
+	/**
+	 * Set whether or not the current reconcile is canceled.
+	 *
+	 * @param isCanceled <code>true</code> iff the current reconcile is canceled
+	 */
+	public void setCanceled(boolean isCanceled) {
+		IDocument document= fSourceViewer != null ? fSourceViewer.getDocument() : null;
+		if (document == null) {
+			fIsCanceled= isCanceled;
+			return;
+		}
+
+		synchronized (getLockObject(document)) {
+			fIsCanceled= isCanceled;
+		}
+	}
+
+	/**
+	 * @param document the document
+	 * @return the document's lock object
+	 */
+	private Object getLockObject(IDocument document) {
+		if (document instanceof ISynchronizable) {
+			Object lock= ((ISynchronizable)document).getLockObject();
+			if (lock != null)
+				return lock;
+		}
+		return document;
+	}
+
+	/**
+	 * Install this presenter on the given source viewer and background presentation
+	 * reconciler.
+	 *
+	 * @param sourceViewer the source viewer
+	 * @param backgroundPresentationReconciler the background presentation reconciler,
+	 * 	can be <code>null</code>, in that case {@link SemanticHighlightingPresenter#createPresentation(List, List)}
+	 * 	should not be called
+	 */
+	public void install(ISourceViewer sourceViewer, StructuredPresentationReconciler backgroundPresentationReconciler) {
+		fSourceViewer= sourceViewer;
+		fPresentationReconciler= backgroundPresentationReconciler;
+
+		if (fSourceViewer instanceof StructuredTextViewer)
+			((StructuredTextViewer) fSourceViewer).prependTextPresentationListener(this);
+		else if(fSourceViewer instanceof ITextViewerExtension4)
+			((ITextViewerExtension4)fSourceViewer).addTextPresentationListener(this);
+
+		fSourceViewer.addTextInputListener(this);
+		manageDocument(fSourceViewer.getDocument());
+	}
+
+	/**
+	 * Uninstall this presenter.
+	 */
+	public void uninstall() {
+		setCanceled(true);
+
+		if (fSourceViewer != null) {
+			if (fSourceViewer instanceof ITextViewerExtension4)
+				((ITextViewerExtension4) fSourceViewer).addTextPresentationListener(this);
+			releaseDocument(fSourceViewer.getDocument());
+			invalidateTextPresentation();
+			resetState();
+
+			fSourceViewer.removeTextInputListener(this);
+			fSourceViewer= null;
+		}
+	}
+
+	/**
+	 * Invalidate text presentation of positions with the given highlighting.
+	 *
+	 * @param highlighting The highlighting
+	 */
+	public void highlightingStyleChanged(HighlightingStyle highlighting) {
+		for (int i= 0, n= fPositions.size(); i < n; i++) {
+			HighlightedPosition position= (HighlightedPosition) fPositions.get(i);
+			if (position.getHighlighting() == highlighting && fSourceViewer instanceof ITextViewerExtension2)
+				((ITextViewerExtension2) fSourceViewer).invalidateTextPresentation(position.getOffset(), position.getLength());
+			else
+				fSourceViewer.invalidateTextPresentation();
+		}
+	}
+
+	/**
+	 * Invalidate text presentation of all positions.
+	 */
+	private void invalidateTextPresentation() {
+		if (fSourceViewer instanceof ITextViewerExtension2) {
+			for (int i = 0, n = fPositions.size(); i < n; i++) {
+				Position position = (Position) fPositions.get(i);
+				((ITextViewerExtension2) fSourceViewer).invalidateTextPresentation(position.getOffset(), position.getLength());
+			}
+		}
+		else {
+			fSourceViewer.invalidateTextPresentation();
+		}
+	}
+
+	/**
+	 * Add a position with the given range and highlighting unconditionally, only from UI thread.
+	 * The position will also be registered on the document. The text presentation is not invalidated.
+	 *
+	 * @param uiPosition the highlighted position to add from the UI
+	 */
+	private void addPositionFromUI(HighlightedPosition uiPosition) {
+		Position position= createHighlightedPosition(uiPosition, uiPosition.getHighlighting(), uiPosition.isReadOnly());
+		synchronized (fPositionLock) {
+			insertPosition(position);
+		}
+
+		IDocument document= fSourceViewer.getDocument();
+		if (document == null)
+			return;
+		String positionCategory= getPositionCategory();
+		try {
+			document.addPosition(positionCategory, position);
+		} catch (BadLocationException e) {
+			// Should not happen
+			Logger.logException(e);
+		} catch (BadPositionCategoryException e) {
+			// Should not happen
+			Logger.logException(e);
+		}
+	}
+
+	/**
+	 * Reset to initial state.
+	 */
+	private void resetState() {
+		synchronized (fPositionLock) {
+			fPositions.clear();
+		}
+	}
+
+	/**
+	 * Start managing the given document.
+	 *
+	 * @param document The document
+	 */
+	private void manageDocument(IDocument document) {
+		if (document != null) {
+			document.addPositionCategory(getPositionCategory());
+			document.addPositionUpdater(fPositionUpdater);
+			document.addDocumentListener(this);
+		}
+	}
+
+	/**
+	 * Stop managing the given document.
+	 *
+	 * @param document The document
+	 */
+	private void releaseDocument(IDocument document) {
+		if (document != null) {
+			document.removeDocumentListener(this);
+			document.removePositionUpdater(fPositionUpdater);
+			try {
+				document.removePositionCategory(getPositionCategory());
+			} catch (BadPositionCategoryException e) {
+				// Should not happen
+				Logger.logException(e);
+			}
+		}
+	}
+
+	/**
+	 * @return The semantic reconciler position's category.
+	 */
+	private String getPositionCategory() {
+		return toString();
+	}
+	
+	private void adjustForeground(StyleRange styleRange) {
+		RGB oldRGB = null;
+		// Color oldColor = styleRange.foreground;
+		Color oldColor = styleRange.background;
+		if (oldColor == null) {
+			// oldRGB = getTextWidget().getForeground().getRGB();
+			oldColor = fSourceViewer.getTextWidget().getBackground();
+			oldRGB = oldColor.getRGB();
+		}
+		else {
+			oldRGB = oldColor.getRGB();
+		}
+		Color newColor = getCachedColorFor(oldRGB);
+		if (newColor == null) {
+			// make text "closer to" background lumanence
+			double target = getRGBConverter().calculateYComponent(oldColor);
+			RGB newRGB = getRGBConverter().transformRGBToGrey(oldRGB, readOnlyForegroundScaleFactor / 100.0, target);
+
+			// save conversion, so calculations only need to be done once
+			cacheColor(oldRGB, newRGB);
+			newColor = getCachedColorFor(oldRGB);
+		}
+		styleRange.foreground = newColor;
+	}
+
+	private YUV_RGBConverter getRGBConverter() {
+		if (rgbConverter == null) {
+			rgbConverter = new YUV_RGBConverter();
+		}
+		return rgbConverter;
+	}
+
+	/**
+	 * Cache read-only color.
+	 * 
+	 * @param oldRGB
+	 * @param newColor
+	 */
+	private void cacheColor(RGB oldRGB, RGB newColor) {
+		if (readOnlyColorTable == null) {
+			readOnlyColorTable = new HashMap();
+		}
+		readOnlyColorTable.put(oldRGB, newColor);
+	}
+
+	/**
+	 * This method is just to get existing read-only colors.
+	 */
+	private Color getCachedColorFor(RGB oldRGB) {
+		Color result = null;
+	
+		if (readOnlyColorTable != null) {
+			RGB readOnlyRGB = (RGB) readOnlyColorTable.get(oldRGB);
+			result = EditorUtility.getColor(readOnlyRGB);
+		}
+	
+		return result;
+	}
+	
+	/**
+	 * A utility class to do various color manipulations
+	 */
+	private class YUV_RGBConverter {
+		/**
+		 * This class "holds" the YUV values corresponding to RGB color
+		 */
+		private class YUV {
+
+			class NormalizedRGB {
+				double blue;
+				double green;
+				private final double maxRGB = 256.0;
+				double red;
+
+				public NormalizedRGB(RGB rgb) {
+					// first normalize to between 0 - 1
+					red = rgb.red / maxRGB;
+					green = rgb.green / maxRGB;
+					blue = rgb.blue / maxRGB;
+
+					red = gammaNormalized(red);
+					green = gammaNormalized(green);
+					blue = gammaNormalized(blue);
+
+				}
+			}
+
+			private NormalizedRGB normalizedRGB;
+
+			private double u = -1;
+			private double v = -1;
+			private double y = -1;
+
+			private YUV() {
+				super();
+			}
+
+			public YUV(RGB rgb) {
+				this();
+				normalizedRGB = new NormalizedRGB(rgb);
+				// force calculations
+				getY();
+				getV();
+				getU();
+			}
+
+			/**
+			 * normalize to "average" gamma 2.2222 or 1/0.45
+			 */
+			double gammaNormalized(double colorComponent) {
+				if (colorComponent < 0.018) {
+					return colorComponent * 0.45;
+				}
+				else {
+					return 1.099 * Math.pow(colorComponent, 0.45) - 0.099;
+				}
+			}
+
+			/**
+			 * @return RGB based on original RGB and current YUV values;
+			 */
+
+			public double getU() {
+				if (u == -1) {
+					u = 0.4949 * (normalizedRGB.blue - getY());
+				}
+				return u;
+
+			}
+
+			public double getV() {
+				if (v == -1) {
+					v = 0.877 * (normalizedRGB.red - getY());
+				}
+				return v;
+			}
+
+			public double getY() {
+				if (y == -1) {
+					y = 0.299 * normalizedRGB.red + 0.587 * normalizedRGB.green + 0.114 * normalizedRGB.blue;
+				}
+				return y;
+			}
+
+		}
+
+		public YUV_RGBConverter() {
+			super();
+		}
+
+		public double calculateYComponent(Color targetColor) {
+			return new YUV(targetColor.getRGB()).getY();
+		}
+
+		public RGB transformRGBToGrey(RGB originalRGB, double scaleFactor, double target) {
+			RGB transformedRGB = null;
+			// we left the "full" API method signature, but this
+			// version does not take into account originalRGB, though
+			// it might someday.
+			// for now, we'll simply make the new RGB grey, either a little
+			// lighter, or a little darker than background.
+			double y = 0;
+			double mid = 0.5;
+			// zero is black, one is white
+			if (target < mid) {
+				// is "dark" make lighter
+				y = target + scaleFactor;
+			}
+			else {
+				// is "light" make darker
+				y = target - scaleFactor;
+			}
+			int c = (int) Math.round(y * 255);
+			// just to gaurd against mis-use, or scale's values greater
+			// than mid point (and possibly rounding error)
+			if (c > 255)
+				c = 255;
+			if (c < 0)
+				c = 0;
+			transformedRGB = new RGB(c, c, c);
+			return transformedRGB;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.wst.standard.schemas/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.standard.schemas/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..8b453e1
--- /dev/null
+++ b/bundles/org.eclipse.wst.standard.schemas/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name.0
+Bundle-SymbolicName: org.eclipse.wst.standard.schemas;singleton:=true
+Bundle-Version: 1.0.401.qualifier
+Bundle-Vendor: %Bundle-Vendor.0
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.wst.xml.core;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.wst.sse.core;bundle-version="[1.1.0,2.0.0)"
diff --git a/bundles/org.eclipse.wst.standard.schemas/plugin.xml b/bundles/org.eclipse.wst.standard.schemas/plugin.xml
new file mode 100644
index 0000000..38ed82d
--- /dev/null
+++ b/bundles/org.eclipse.wst.standard.schemas/plugin.xml
@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+
+
+    <!--==========================================-->
+    <!-- Catalog Contributions                    -->
+    <!-- Register well known Web DTDs and XSDs    -->
+    <!--==========================================-->
+
+    <!-- new form -->
+
+    <!-- 
+        See https://bugs.eclipse.org/bugs/show_bug.cgi?id=100481
+        you may have to comment out following extension if used 
+        in development environment. 
+        
+    -->
+
+    <extension point="org.eclipse.wst.xml.core.catalogContributions">
+        <catalogContribution id="default">
+
+
+            <public
+                publicId="-//WAPFORUM//DTD WML 1.1//EN"
+                uri="dtds/wml11.dtd">
+            </public>
+
+            <public
+                publicId="-//W3C//DTD XHTML 1.0 Strict//EN"
+                uri="dtds/xhtml1-strict.dtd"
+                webURL="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+            </public>
+
+            <public
+                publicId="-//W3C//DTD XHTML 1.0 Transitional//EN"
+                uri="dtds/xhtml1-transitional.dtd"
+                webURL="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+            </public>
+            <public
+                publicId="-//W3C//DTD XHTML 1.0 Frameset//EN"
+                uri="dtds/xhtml1-frameset.dtd"
+                webURL="http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+            </public>
+
+            <public
+                publicId="-//W3C//DTD XHTML Basic 1.0//EN"
+                uri="dtds/xhtml-basic10-f.dtd"
+                webURL="http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">
+            </public>
+
+            <public
+                publicId="-//W3C//DTD XHTML 1.1//EN"
+                uri="dtds/xhtml11-flat.dtd"
+                webURL="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+            </public>
+
+            <public
+                publicId="-//WAPFORUM//DTD XHTML Mobile 1.0//EN"
+                uri="dtds/xhtml-mobile10-flat.dtd"
+                webURL="http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
+            </public>
+
+            <public
+                publicId="-//WAPFORUM//DTD WML 1.3//EN"
+                uri="dtds/wml13.dtd"
+                webURL="http://www.wapforum.org/DTD/wml13.dtd">
+            </public>
+            
+            
+            <public
+                publicId="-//W3C//DTD HTML 4.01 Frameset//EN"
+                uri="dtds/frameset.dtd"
+                webURL="http://www.w3.org/TR/html4/frameset.dtd">
+            </public>
+            
+            <public
+                publicId="-//W3C//ENTITIES Latin 1//EN//HTML"
+                uri="dtds/HTMLlat1.ent"
+                webURL="HTMLlat1.ent">
+            </public>
+            
+            <public
+                publicId="-//W3C//ENTITIES Special//EN//HTM"
+                uri="dtds/HTMLspecial.ent"
+                webURL="HTMLspecial.ent">
+            </public>
+            
+            <public
+                publicId="-//W3C//ENTITIES Symbols//EN//HTML"
+                uri="dtds/HTMLsymbol.ent"
+                webURL="HTMLsymbol.ent">
+            </public>
+            
+            <public
+                publicId="-//W3C//DTD HTML 4.01 Transitional//EN"
+                uri="dtds/loose.dtd"
+                webURL="http://www.w3.org/TR/html4/loose.dtd">
+            </public>
+        
+            <public
+                publicId="-//W3C//DTD HTML 4.01//EN"
+                uri="dtds/strict.dtd"
+                webURL="http://www.w3.org/TR/html4/strict.dtd">
+            </public>
+
+            <uri
+                name="http://schemas.xmlsoap.org/wsdl/"
+                uri="xsd/wsdl.xsd" />
+            <uri
+                name="http://schemas.xmlsoap.org/wsdl/soap/"
+                uri="xsd/soap.xsd" />
+            <uri
+                name="http://schemas.xmlsoap.org/wsdl/http/"
+                uri="xsd/http.xsd" />
+            <uri
+                name="http://schemas.xmlsoap.org/wsdl/mime/"
+                uri="xsd/wsdl-mime.xsd" />
+            <uri
+                name="http://schemas.xmlsoap.org/soap/encoding/"
+                uri="xsd/soapenc.xsd" />
+            <uri
+                name="http://schemas.xmlsoap.org/soap/envelope/"
+                uri="xsd/soapenv.xsd" />
+            <uri
+                name="urn:oasis:names:tc:entity:xmlns:xml:catalog"
+                uri="xsd/xmlcatalog11.xsd" />
+            <uri
+                name="http://www.w3.org/TR/html4/loose.dtd"
+                uri="dtds/loose.dtd"/>
+            <uri
+                name="http://www.w3.org/TR/html4/strict.dtd"
+                uri="dtds/strict.dtd"/>
+
+
+            <!-- these are removed from the catalog, since they are -not- 
+            the same as those in the xsd.ui plugin, which 
+            point to the org.eclipse.xsd versions
+            <uri
+                name="http://www.w3.org/2001/XMLSchema"
+                uri="dtdsAndSchemas/XMLSchema.dtd" />
+                
+            <system
+                systemId="http://www.w3.org/2001/xml.xsd"
+                uri="dtdsAndSchemas/xml.xsd" />
+             -->
+        </catalogContribution>
+
+    </extension>
+
+
+
+
+    <extension point="org.eclipse.wst.sse.core.documentTypes">
+        <documentType
+            elementName="html"
+            displayName="%documentType.displayName.0"
+            namespaceURI="http://www.w3.org/1999/xhtml"
+            isXHTML="true"
+            hasFrameset="false"
+            publicID="-//W3C//DTD XHTML 1.0 Strict//EN"
+            systemID="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+        </documentType>
+        <documentType
+            elementName="html"
+            displayName="%documentType.displayName.1"
+            namespaceURI="http://www.w3.org/1999/xhtml"
+            isXHTML="true"
+            hasFrameset="false"
+            publicID="-//W3C//DTD XHTML 1.0 Transitional//EN"
+            defaultXHTML="true"
+            systemID="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+        </documentType>
+        <documentType
+            elementName="html"
+            displayName="%documentType.displayName.2"
+            namespaceURI="http://www.w3.org/1999/xhtml"
+            isXHTML="true"
+            hasFrameset="true"
+            publicID="-//W3C//DTD XHTML 1.0 Frameset//EN"
+            systemID="http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+        </documentType>
+        <documentType
+            elementName="html"
+            displayName="%documentType.displayName.3"
+            namespaceURI="http://www.w3.org/1999/xhtml"
+            isXHTML="true"
+            hasFrameset="false"
+            publicID="-//W3C//DTD XHTML Basic 1.0//EN"
+            systemID="http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">
+        </documentType>
+        <documentType
+            elementName="html"
+            displayName="%documentType.displayName.4"
+            namespaceURI="http://www.w3.org/1999/xhtml"
+            isXHTML="true"
+            hasFrameset="false"
+            publicID="-//W3C//DTD XHTML 1.1//EN"
+            systemID="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+        </documentType>
+        <documentType
+            elementName="html"
+            displayName="%documentType.displayName.5"
+            namespaceURI="http://www.w3.org/1999/xhtml"
+            isXHTML="true"
+            hasFrameset="false"
+            publicID="-//WAPFORUM//DTD XHTML Mobile 1.0//EN"
+            systemID="http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
+        </documentType>
+        <documentType
+            elementName="wml"
+            displayName="%documentType.displayName.6"
+            namespaceURI=""
+            isWML="true"
+            hasFrameset="false"
+            publicID="-//WAPFORUM//DTD WML 1.3//EN"
+            defaultWML="true"
+            systemID="http://www.wapforum.org/DTD/wml13.dtd">
+        </documentType>
+    </extension>
+
+</plugin>
diff --git a/bundles/org.eclipse.wst.xml.core/schema/externalSchemaLocations.exsd b/bundles/org.eclipse.wst.xml.core/schema/externalSchemaLocations.exsd
new file mode 100644
index 0000000..0fe2a90
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.core/schema/externalSchemaLocations.exsd
@@ -0,0 +1,102 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.wst.xml.core" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.wst.xml.core" id="externalSchemaLocations" name="External Schema Locations"/>
+      </appInfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appInfo>
+            <meta.element />
+         </appInfo>
+      </annotation>
+      <complexType>
+         <sequence minOccurs="1" maxOccurs="unbounded">
+            <element ref="provider"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="provider">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn=":org.eclipse.wst.xml.core.contentmodel.modelquery.IExternalSchemaLocationProvider"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiinfo"/>
+      </appInfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+
+</schema>
diff --git a/bundles/org.eclipse.wst.xml.core/schema/modelQueryExtensions.exsd b/bundles/org.eclipse.wst.xml.core/schema/modelQueryExtensions.exsd
new file mode 100644
index 0000000..80f8365
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.core/schema/modelQueryExtensions.exsd
@@ -0,0 +1,102 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.wst.xml.core" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.wst.xml.core" id="modelQueryExtensions" name="Model Query Extension"/>
+      </appInfo>
+      <documentation>
+         &lt;b&gt;This extension point is internal and should not be used by any other plugins.&lt;/b&gt;
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appInfo>
+            <meta.element internal="true" />
+         </appInfo>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="modelQueryExtension"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  a fully qualified identifier of the target extension point
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  an optional identifier of the extension instance
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  an optional name of the extension instance
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="modelQueryExtension">
+      <annotation>
+         <documentation>
+            Allows for the customization of returned vaues from a ModelQuery object.
+         </documentation>
+      </annotation>
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn="org.eclipse.wst.xml.core.internal.contentmodel.modelquery.extension.ModelQueryExtension:"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+         <attribute name="contentType" type="string" use="required">
+            <annotation>
+               <documentation>
+                  A comma-separated list of content types for which to use this extension.
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         &lt;b&gt;This extension point is internal and should not be used by any other plugins.&lt;/b&gt;
+      </documentation>
+   </annotation>
+
+
+
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         Copyright (c) 2000, 2005 IBM Corporation and others.&lt;br&gt;
+All rights reserved. This program and the accompanying materials are made 
+available under the terms of the Eclipse Public License v1.0 which accompanies 
+this distribution, and is available at &lt;a
+href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/bundles/org.eclipse.wst.xml.core/src-contentmodel/org/eclipse/wst/xml/core/internal/contentmodel/internal/modelqueryimpl/ModelQueryExtensionRegistry.java b/bundles/org.eclipse.wst.xml.core/src-contentmodel/org/eclipse/wst/xml/core/internal/contentmodel/internal/modelqueryimpl/ModelQueryExtensionRegistry.java
new file mode 100644
index 0000000..1c98b44
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.core/src-contentmodel/org/eclipse/wst/xml/core/internal/contentmodel/internal/modelqueryimpl/ModelQueryExtensionRegistry.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.core.internal.contentmodel.internal.modelqueryimpl;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.wst.sse.core.utils.StringUtils;
+import org.eclipse.wst.xml.core.internal.Logger;
+import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.extension.ModelQueryExtension;
+
+public class ModelQueryExtensionRegistry
+{
+  protected static final String EXTENSION_POINT_ID = "modelQueryExtensions"; //$NON-NLS-1$
+  protected static final String TAG_NAME = "modelQueryExtension"; //$NON-NLS-1$
+  private List descriptors;
+
+  public List getApplicableExtensions(String contentTypeId, String namespace)
+  {
+    List list = new ArrayList();
+    if (contentTypeId != null)
+    {
+      ensureExtensionsLoaded();
+      for (Iterator i = descriptors.iterator(); i.hasNext();)
+      {
+        ModelQueryExtensionDescriptor descriptor = (ModelQueryExtensionDescriptor) i.next();
+        final String[] contentTypeIds = StringUtils.unpack(descriptor.getContentTypeId());
+        for (int j = 0; j < contentTypeIds.length; j++) {
+	        if (contentTypeId.equals(contentTypeIds[j]))
+	        {
+	          if (descriptor.getNamespace() == null ||  descriptor.getNamespace().equals(namespace))
+	          {  
+	            try
+	            {
+	              ModelQueryExtension extension = descriptor.createModelQueryExtension();
+	              list.add(extension);
+	            }
+	            catch (CoreException e) {
+	            	Logger.logException("problem creating model query extension", e); //$NON-NLS-1$
+	            }
+	          }
+	          break;
+	        }
+        }
+      }
+    }
+    return list;
+  }
+  
+  /*
+   *  TODO : consider providing a non-plugin means add/remove extensions
+   *   
+  public void addExtension(ModelQueryExtension extension)
+  {     
+  }
+
+  public void removeExtension(ModelQueryExtensionDeprecated extension)
+  {
+  }*/
+
+  /**
+   * Reads all extensions.
+   * <p>
+   * This method can be called more than once in order to reload from a changed
+   * extension registry.
+   * </p>
+   */
+  private synchronized void reloadExtensions()
+  {
+    descriptors = new ArrayList();
+    String bundleid = "org.eclipse.wst.xml.core"; //$NON-NLS-1$      
+    IConfigurationElement[] elements = Platform.getExtensionRegistry().getConfigurationElementsFor(bundleid, EXTENSION_POINT_ID);
+    for (int i = 0; i < elements.length; i++)
+    {
+      ModelQueryExtensionDescriptor descriptor = new ModelQueryExtensionDescriptor(elements[i]);
+      descriptors.add(descriptor);
+    }
+  }
+
+  /**
+   * Ensures the extensions have been loaded at least once.
+   */
+  private void ensureExtensionsLoaded()
+  {
+    if (descriptors == null)
+    {
+      reloadExtensions();
+    }
+  }
+}
diff --git a/bundles/org.eclipse.wst.xml.core/src-search/org/eclipse/wst/xml/core/internal/search/XMLSearchPattern.java b/bundles/org.eclipse.wst.xml.core/src-search/org/eclipse/wst/xml/core/internal/search/XMLSearchPattern.java
new file mode 100644
index 0000000..7bc2a6a
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.core/src-search/org/eclipse/wst/xml/core/internal/search/XMLSearchPattern.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.wst.xml.core.internal.search;
+
+import org.eclipse.wst.common.core.search.pattern.SearchPattern;
+import org.eclipse.wst.xml.core.internal.search.matching.SAXSearchElement;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import com.ibm.icu.util.StringTokenizer;
+
+public class XMLSearchPattern extends SearchPattern{
+	
+	private String elementNamespace = null;
+	private String elementName = null;
+	private String attributeName = null;
+	private String searchName = null;
+	private String searchNamespace = null;
+	private String parentName = null;
+    private int depth = -1;
+    private SAXSearchElement element = null;
+    private Element domElement = null;
+
+    public XMLSearchPattern(String elementNamespace, String parentElementName,String elementName, String attributeName) {
+    	this(elementNamespace, parentElementName, elementName, attributeName, -1);
+    }
+	
+	public XMLSearchPattern(String elementNamespace, String elementName, String attributeName) {
+       this(elementNamespace, null, elementName, attributeName, -1);
+    }
+    
+    public XMLSearchPattern(String elementNamespace, String elementName, String attributeName, int depth) {
+    	this(elementNamespace, null, elementName, attributeName, depth);       
+    }
+           
+    private XMLSearchPattern(String elementNamespace, String parentElementName, String elementName, String attributeName, int depth) {
+        super();
+        this.attributeName = attributeName;
+        this.elementName = elementName;
+        this.parentName = parentElementName;
+        this.elementNamespace = elementNamespace;
+        this.depth = depth;
+    }
+    
+	public XMLSearchPattern(){
+		
+	}
+	
+	public String getAttributeName() {
+		return attributeName;
+	}
+
+	public String getElementName() {
+		return elementName;
+	}
+
+	public String getElementNamespace() {
+		return elementNamespace;
+	}
+
+	public String getSearchName() {
+		return searchName;
+	}
+
+	public String getSearchNamespace() {
+		return searchNamespace;
+	}
+
+	public void setSearchName(String searchName) {
+		this.searchName = searchName;
+	}
+
+	public void setSearchNamespace(String searchNamespace) {
+		this.searchNamespace = searchNamespace;
+	}
+
+	public void setAttributeName(String attributeName) {
+		this.attributeName = attributeName;
+	}
+
+	public void setElementName(String elementName) {
+		this.elementName = elementName;
+	}
+
+	public void setElementNamespace(String elementNamespace) {
+		this.elementNamespace = elementNamespace;
+	}
+
+	public void setSearchElement(SAXSearchElement element) {
+		this.element = element;
+	}
+
+	public void setSearchElement(Element element) {
+		domElement = element;
+	}
+
+	public boolean matches(XMLSearchPattern pattern) {
+		if (pattern.searchName == null)
+			return false;
+		if ("*".equals(searchName) && "*".equals(searchNamespace)) { //$NON-NLS-1$ //$NON-NLS-2$
+			return true;
+		}
+
+		final StringTokenizer tokenizer = new StringTokenizer(pattern.searchName);
+		 while (tokenizer.hasMoreTokens()) {
+			 final String token = tokenizer.nextToken();
+			 int n = token.indexOf(":"); //$NON-NLS-1$
+			 String name = token;
+			 String namespace = pattern.searchNamespace; // accept the default
+			 if(n > 0) {
+				 final String prefix = token.substring(0, n);
+				 name = token.substring(n+1);
+				 namespace = pattern.element != null ? (String)pattern.element.getNamespaceMap().get(prefix) : computeNamespaceForPrefix(pattern.domElement, prefix);
+			 }
+
+			 if (namespace == null) {
+				 if (name.equals(searchName) || "*".equals(searchName))
+					 return true;
+			 }
+			 else {
+				 if ((namespace.equals(searchNamespace) || "*".equals(searchNamespace)) && name.equals(searchName))
+					 return true;
+			 }
+		 }
+		 return false;
+	}
+
+	protected String computeNamespaceForPrefix(Element element, String prefix)
+	{
+	  String result = null;
+	  for (Node node = element; node != null; node = node.getParentNode())
+	  {
+		if (node.getNodeType() == Node.ELEMENT_NODE)
+		{
+		  Element e = (Element)node;
+		  String attributeName = (prefix != null && prefix.length() > 0) ? ("xmlns:" + prefix) : "xmlns";  //$NON-NLS-1$ //$NON-NLS-2$
+		  result = e.hasAttribute(attributeName) ? e.getAttribute(attributeName) : null;
+		  if (result != null && result.length() > 0)
+		  {
+			 break;  
+		  }	  
+		}	
+	  }	  
+	  return result;
+	}
+
+  public int getDepth()
+  {
+    return depth;
+  }
+
+  public void setDepth(int depth)
+  {
+    this.depth = depth;
+  }
+
+public String getParentName() {
+	return parentName;
+}
+
+public void setParentName(String parentName) {
+	this.parentName = parentName;
+}
+	
+
+	
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeContainer.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeContainer.java
new file mode 100644
index 0000000..ac7a403
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeContainer.java
@@ -0,0 +1,534 @@
+/*******************************************************************************
+ * Copyright (c) 2001, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Jens Lukowski/Innoopract - initial renaming/restructuring
+ *     David Carver (STAR) - bug 296999 - Inefficient use of new String()
+ *******************************************************************************/
+package org.eclipse.wst.xml.core.internal.document;
+
+
+
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+/**
+ * NodeContainer class
+ */
+public abstract class NodeContainer extends NodeImpl implements Node, NodeList {
+
+	/**
+	 */
+	private class ChildNodesCache implements NodeList {
+		private Node curChild = null;
+		private int curIndex = -1;
+		private int length = 0;
+
+		ChildNodesCache() {
+			initializeCache();
+		}
+
+		public int getLength() {
+			// atomic
+			return this.length;
+		}
+
+		private void initializeCache() {
+			// note we use the outter objects lockobject
+			// (since we are using their "children".
+			synchronized (lockObject) {
+				for (Node child = firstChild; child != null; child = child.getNextSibling()) {
+					this.length++;
+				}
+			}
+		}
+
+		public Node item(int index) {
+			synchronized (lockObject) {
+				if (this.length == 0)
+					return null;
+				if (index < 0)
+					return null;
+				if (index >= this.length)
+					return null;
+
+				if (this.curIndex < 0) { // first time
+					if (index * 2 >= this.length) { // search from the last
+						this.curIndex = this.length - 1;
+						this.curChild = lastChild;
+					} else { // search from the first
+						this.curIndex = 0;
+						this.curChild = firstChild;
+					}
+				}
+
+				if (index == this.curIndex)
+					return this.curChild;
+
+				if (index > this.curIndex) {
+					while (index > this.curIndex) {
+						this.curIndex++;
+						this.curChild = this.curChild.getNextSibling();
+					}
+				} else { // index < this.curIndex
+					while (index < this.curIndex) {
+						this.curIndex--;
+						this.curChild = this.curChild.getPreviousSibling();
+					}
+				}
+
+				return this.curChild;
+			}
+		}
+	}
+
+	private NodeList childNodesCache = null;
+
+	private boolean fChildEditable = true;
+	NodeImpl firstChild = null;
+	NodeImpl lastChild = null;
+
+	Object lockObject = new byte[0];
+
+	/**
+	 * NodeContainer constructor
+	 */
+	protected NodeContainer() {
+		super();
+	}
+
+	/**
+	 * NodeContainer constructor
+	 * 
+	 * @param that
+	 *            NodeContainer
+	 */
+	protected NodeContainer(NodeContainer that) {
+		super(that);
+	}
+
+	/**
+	 * appendChild method
+	 * 
+	 * @return org.w3c.dom.Node
+	 * @param newChild
+	 *            org.w3c.dom.Node
+	 */
+	public Node appendChild(Node newChild) throws DOMException {
+		return insertBefore(newChild, null);
+	}
+
+	/**
+	 * cloneChildNodes method
+	 * 
+	 * @param container
+	 *            org.w3c.dom.Node
+	 * @param deep
+	 *            boolean
+	 */
+	protected void cloneChildNodes(Node newParent, boolean deep) {
+		if (newParent == null || newParent == this)
+			return;
+		if (!(newParent instanceof NodeContainer))
+			return;
+
+		NodeContainer container = (NodeContainer) newParent;
+		container.removeChildNodes();
+
+		for (Node child = getFirstChild(); child != null; child = child.getNextSibling()) {
+			Node cloned = child.cloneNode(deep);
+			if (cloned != null)
+				container.appendChild(cloned);
+		}
+	}
+
+	/**
+	 * getChildNodes method
+	 * 
+	 * @return org.w3c.dom.NodeList
+	 */
+	public NodeList getChildNodes() {
+		return this;
+	}
+
+	/**
+	 * getFirstChild method
+	 * 
+	 * @return org.w3c.dom.Node
+	 */
+	public Node getFirstChild() {
+		return this.firstChild;
+	}
+
+	/**
+	 * getLastChild method
+	 * 
+	 * @return org.w3c.dom.Node
+	 */
+	public Node getLastChild() {
+		return this.lastChild;
+	}
+
+	/**
+	 * getLength method
+	 * 
+	 * @return int
+	 */
+	public int getLength() {
+		if (this.firstChild == null)
+			return 0;
+		synchronized (lockObject) {
+			if (this.childNodesCache == null)
+				this.childNodesCache = new ChildNodesCache();
+			return this.childNodesCache.getLength();
+		}
+	}
+
+	/**
+	 */
+	public String getSource() {
+		StringBuffer buffer = new StringBuffer();
+
+		IStructuredDocumentRegion startStructuredDocumentRegion = getStartStructuredDocumentRegion();
+		if (startStructuredDocumentRegion != null) {
+			String source = startStructuredDocumentRegion.getText();
+			if (source != null)
+				buffer.append(source);
+		}
+
+		for (NodeImpl child = firstChild; child != null; child = (NodeImpl) child.getNextSibling()) {
+			String source = child.getSource();
+			if (source != null)
+				buffer.append(source);
+		}
+
+		IStructuredDocumentRegion endStructuredDocumentRegion = getEndStructuredDocumentRegion();
+		if (endStructuredDocumentRegion != null) {
+			String source = endStructuredDocumentRegion.getText();
+			if (source != null)
+				buffer.append(source);
+		}
+
+		return buffer.toString();
+	}
+
+	/**
+	 * hasChildNodes method
+	 * 
+	 * @return boolean
+	 */
+	public boolean hasChildNodes() {
+		return (this.firstChild != null);
+	}
+
+	/**
+	 * insertBefore method
+	 * 
+	 * @return org.w3c.dom.Node
+	 * @param newChild
+	 *            org.w3c.dom.Node
+	 * @param refChild
+	 *            org.w3c.dom.Node
+	 */
+	public Node insertBefore(Node newChild, Node refChild) throws DOMException {
+		if (newChild == null)
+			return null; // nothing to do
+		if (refChild != null && refChild.getParentNode() != this) {
+			throw new DOMException(DOMException.NOT_FOUND_ERR, DOMMessages.NOT_FOUND_ERR);
+		}
+		if (!isChildEditable()) {
+			throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, DOMMessages.NO_MODIFICATION_ALLOWED_ERR);
+		}
+		if (newChild == refChild)
+			return newChild; // nothing to do
+		//new child can not be a parent of this, would cause cycle
+		if(isParent(newChild)) {
+			throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, DOMMessages.HIERARCHY_REQUEST_ERR);
+		}
+
+		if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
+			// insert child nodes instead
+			for (Node child = newChild.getFirstChild(); child != null; child = newChild.getFirstChild()) {
+				newChild.removeChild(child);
+				insertBefore(child, refChild);
+			}
+			return newChild;
+		}
+		// synchronized in case another thread is getting item, or length
+		synchronized (lockObject) {
+			this.childNodesCache = null; // invalidate child nodes cache
+		}
+
+		NodeImpl child = (NodeImpl) newChild;
+		NodeImpl next = (NodeImpl) refChild;
+		NodeImpl prev = null;
+		Node oldParent = child.getParentNode();
+		if (oldParent != null)
+			oldParent.removeChild(child);
+		if (next == null) {
+			prev = this.lastChild;
+			this.lastChild = child;
+		} else {
+			prev = (NodeImpl) next.getPreviousSibling();
+			next.setPreviousSibling(child);
+		}
+		if (prev == null)
+			this.firstChild = child;
+		else
+			prev.setNextSibling(child);
+		child.setPreviousSibling(prev);
+		child.setNextSibling(next);
+		child.setParentNode(this);
+		// make sure having the same owner document
+		if (child.getOwnerDocument() == null) {
+			if (getNodeType() == DOCUMENT_NODE) {
+				child.setOwnerDocument((Document) this);
+			} else {
+				child.setOwnerDocument(getOwnerDocument());
+			}
+		}
+
+		notifyChildReplaced(child, null);
+
+		return child;
+	}
+
+	public boolean isChildEditable() {
+		if (!fChildEditable) {
+			DOMModelImpl model = (DOMModelImpl) getModel();
+			if (model != null && model.isReparsing()) {
+				return true;
+			}
+		}
+		return fChildEditable;
+	}
+
+	/**
+	 * isContainer method
+	 * 
+	 * @return boolean
+	 */
+	public boolean isContainer() {
+		return true;
+	}
+
+	/**
+	 * item method
+	 * 
+	 * @return org.w3c.dom.Node
+	 * @param index
+	 *            int
+	 */
+	public Node item(int index) {
+		if (this.firstChild == null)
+			return null;
+		synchronized (lockObject) {
+			if (this.childNodesCache == null)
+				this.childNodesCache = new ChildNodesCache();
+			return this.childNodesCache.item(index);
+		}
+	}
+
+	/**
+	 * notifyChildReplaced method
+	 * 
+	 * @param newChild
+	 *            org.w3c.dom.Node
+	 * @param oldChild
+	 *            org.w3c.dom.Node
+	 */
+	protected void notifyChildReplaced(Node newChild, Node oldChild) {
+		DocumentImpl document = (DocumentImpl) getContainerDocument();
+		if (document == null)
+			return;
+
+		syncChildEditableState(newChild);
+
+		DOMModelImpl model = (DOMModelImpl) document.getModel();
+		if (model == null)
+			return;
+		model.childReplaced(this, newChild, oldChild);
+	}
+
+	/**
+	 * removeChild method
+	 * 
+	 * @return org.w3c.dom.Node
+	 * @param oldChild
+	 *            org.w3c.dom.Node
+	 */
+	public Node removeChild(Node oldChild) throws DOMException {
+		if (oldChild == null)
+			return null;
+		if (oldChild.getParentNode() != this) {
+			throw new DOMException(DOMException.NOT_FOUND_ERR, DOMMessages.NOT_FOUND_ERR);
+		}
+
+		if (!isChildEditable()) {
+			throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, DOMMessages.NO_MODIFICATION_ALLOWED_ERR);
+		}
+
+		// synchronized in case another thread is getting item, or length
+		synchronized (lockObject) {
+			this.childNodesCache = null; // invalidate child nodes cache
+		}
+
+		NodeImpl child = (NodeImpl) oldChild;
+		NodeImpl prev = (NodeImpl) child.getPreviousSibling();
+		NodeImpl next = (NodeImpl) child.getNextSibling();
+
+		child.setEditable(true, true); // clear ReadOnly flags
+
+		if (prev == null)
+			this.firstChild = next;
+		else
+			prev.setNextSibling(next);
+		if (next == null)
+			this.lastChild = prev;
+		else
+			next.setPreviousSibling(prev);
+		child.setPreviousSibling(null);
+		child.setNextSibling(null);
+		child.setParentNode(null);
+
+		notifyChildReplaced(null, child);
+
+		return child;
+	}
+
+	/**
+	 * removeChildNodes method
+	 */
+	public void removeChildNodes() {
+		if (!isChildEditable()) {
+			throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, DOMMessages.NO_MODIFICATION_ALLOWED_ERR);
+		}
+
+		Node nextChild = null;
+		for (Node child = getFirstChild(); child != null; child = nextChild) {
+			nextChild = child.getNextSibling();
+			removeChild(child);
+		}
+	}
+
+	/**
+	 * removeChildNodes method
+	 * 
+	 * @return org.w3c.dom.DocumentFragment
+	 * @param firstChild
+	 *            org.w3c.dom.Node
+	 * @param lastChild
+	 *            org.w3c.dom.Node
+	 */
+	public DocumentFragment removeChildNodes(Node firstChild, Node lastChild) {
+		if (!hasChildNodes())
+			return null;
+		if (!isChildEditable()) {
+			throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, DOMMessages.NO_MODIFICATION_ALLOWED_ERR);
+		}
+
+		Document document = null;
+		if (getNodeType() == DOCUMENT_NODE)
+			document = (Document) this;
+		else
+			document = getOwnerDocument();
+		if (document == null)
+			return null;
+		DocumentFragment fragment = document.createDocumentFragment();
+		if (fragment == null)
+			return null;
+
+		if (firstChild == null)
+			firstChild = getFirstChild();
+		if (lastChild == null)
+			lastChild = getLastChild();
+		Node nextChild = null;
+		for (Node child = firstChild; child != null; child = nextChild) {
+			nextChild = child.getNextSibling();
+			removeChild(child);
+			fragment.appendChild(child);
+			if (child == lastChild)
+				break;
+		}
+
+		return fragment;
+	}
+
+	/**
+	 * replaceChild method
+	 * 
+	 * @return org.w3c.dom.Node
+	 * @param newChild
+	 *            org.w3c.dom.Node
+	 * @param oldChild
+	 *            org.w3c.dom.Node
+	 */
+	public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
+		if (!isChildEditable()) {
+			throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, DOMMessages.NO_MODIFICATION_ALLOWED_ERR);
+		}
+
+		if (oldChild == null || oldChild == newChild)
+			return newChild;
+		if (newChild != null)
+			insertBefore(newChild, oldChild);
+		return removeChild(oldChild);
+	}
+
+	public void setChildEditable(boolean editable) {
+		if (fChildEditable == editable) {
+			return;
+		}
+
+		ReadOnlyController roc = ReadOnlyController.getInstance();
+		Node node;
+		if (editable) {
+			for (node = getFirstChild(); node != null; node = node.getNextSibling()) {
+				roc.unlockNode((IDOMNode) node);
+			}
+		} else {
+			for (node = getFirstChild(); node != null; node = node.getNextSibling()) {
+				roc.lockNode((IDOMNode) node);
+			}
+		}
+
+		fChildEditable = editable;
+		notifyEditableChanged();
+	}
+
+	protected void syncChildEditableState(Node child) {
+		ReadOnlyController roc = ReadOnlyController.getInstance();
+		if (fChildEditable) {
+			roc.unlockNode((NodeImpl) child);
+		} else {
+			roc.lockNode((NodeImpl) child);
+		}
+	}
+
+	/**
+	 * <p>Checks to see if the given <code>Node</code> is a parent of <code>this</code> node</p>
+	 * 
+	 * @param possibleParent the possible parent <code>Node</code> of <code>this</code> node
+	 * @return <code>true</code> if <code>possibleParent</code> is the parent of <code>this</code>,
+	 * <code>false</code> otherwise.
+	 */
+	private boolean isParent(Node possibleParent) {
+		Node parent = this.getParentNode();
+		while(parent != null && parent != possibleParent) {
+			parent = parent.getParentNode();
+		}
+
+		return parent == possibleParent;
+	}
+}
diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/formatter/DefaultXMLPartitionFormatter.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/formatter/DefaultXMLPartitionFormatter.java
new file mode 100644
index 0000000..9537d42
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/formatter/DefaultXMLPartitionFormatter.java
@@ -0,0 +1,1713 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     David Carver (STAR) - bug 297006 - String Comparison
+ *******************************************************************************/
+package org.eclipse.wst.xml.core.internal.formatter;
+
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.text.edits.DeleteEdit;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+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.Logger;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMDataType;
+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.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMText;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+import org.eclipse.wst.xml.core.internal.ssemodelquery.ModelQueryAdapter;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class DefaultXMLPartitionFormatter {
+	/**
+	 * Just a small container class that holds a DOMNode & documentRegion that
+	 * should represent each other.
+	 */
+	protected class DOMRegion {
+		public IDOMNode domNode;
+		public IStructuredDocumentRegion documentRegion;
+	}
+
+	static private final String PRESERVE = "preserve";//$NON-NLS-1$
+	static private final String COLLAPSE = "collapse";//$NON-NLS-1$
+	static private final String REPLACE = "replace";//$NON-NLS-1$
+	static private final String PRESERVE_QUOTED = "\"preserve\"";//$NON-NLS-1$
+	static private final String XML_SPACE = "xml:space";//$NON-NLS-1$
+	static private final String XSL_NAMESPACE = "http://www.w3.org/1999/XSL/Transform"; //$NON-NLS-1$
+	static private final String XSL_ATTRIBUTE = "attribute"; //$NON-NLS-1$
+	static private final String XSL_TEXT = "text"; //$NON-NLS-1$
+	static private final String SPACE = " "; //$NON-NLS-1$
+	static private final String EMPTY = ""; //$NON-NLS-1$
+	static private final String PROPERTY_WHITESPACE_FACET = "org.eclipse.wst.xsd.cm.properties/whitespace"; //$NON-NLS-1$
+
+	private XMLFormattingPreferences fPreferences = null;
+	private IProgressMonitor fProgressMonitor;
+
+	private int replaceSpaces(TextEdit textEdit, int spaceStartOffset, int availableLineWidth, String whitespaceRun) {
+		StringBuffer buff = new StringBuffer(whitespaceRun);
+		for(int i = 0; i < buff.length(); i++) {
+			buff.setCharAt(i, ' '); //$NON-NLS-1$
+		}
+		String replacementString = buff.toString();
+		if (!replacementString.equals(whitespaceRun)) {
+			ReplaceEdit replaceEdit = new ReplaceEdit(spaceStartOffset, whitespaceRun.length(), replacementString);
+			textEdit.addChild(replaceEdit);
+		}
+		return availableLineWidth;
+	}
+	
+	private int collapseSpaces(TextEdit textEdit, int spaceStartOffset, int availableLineWidth, String whitespaceRun) {
+		// prefer to use use existing whitespace
+		int existingWhitespaceOffset = whitespaceRun.indexOf(' ');
+		if (existingWhitespaceOffset > -1) {
+			// delete whitespaces before and after existing whitespace
+			if (existingWhitespaceOffset > 0) {
+				DeleteEdit deleteEdit = new DeleteEdit(spaceStartOffset, existingWhitespaceOffset);
+				textEdit.addChild(deleteEdit);
+			}
+			if (existingWhitespaceOffset < whitespaceRun.length() - 1) {
+				int nextOffset = existingWhitespaceOffset + 1;
+				DeleteEdit deleteEdit = new DeleteEdit(spaceStartOffset + nextOffset, whitespaceRun.length() - nextOffset);
+				textEdit.addChild(deleteEdit);
+			}
+		}
+		else {
+			// delete all whitespace and insert new one
+			// collapse whitespace by deleting whitespace
+			DeleteEdit deleteEdit = new DeleteEdit(spaceStartOffset, whitespaceRun.length());
+			textEdit.addChild(deleteEdit);
+			// then insert one space
+			InsertEdit insertEdit = new InsertEdit(spaceStartOffset, SPACE);
+			textEdit.addChild(insertEdit);
+		}
+		// remember to account for space added
+		--availableLineWidth;
+		return availableLineWidth;
+	}
+	
+	private int collapseAndIndent(TextEdit textEdit, int spaceStartOffset, int availableLineWidth, int indentLevel, String whitespaceRun, IStructuredDocumentRegion currentRegion) {
+		// Need to keep blank lines, but still collapse the whitespace
+		String lineDelimiters = null;
+		if (!getFormattingPreferences().getClearAllBlankLines()) {
+			lineDelimiters = extractLineDelimiters(whitespaceRun, currentRegion);
+			String formattedLine = lineDelimiters + getIndentString(indentLevel);
+			if(lineDelimiters.length() > 0 && !formattedLine.equals(whitespaceRun)) {
+				textEdit.addChild(new ReplaceEdit(spaceStartOffset, whitespaceRun.length(), formattedLine));
+				availableLineWidth = getFormattingPreferences().getMaxLineWidth() - indentLevel;
+			}
+		}
+		if (lineDelimiters == null || lineDelimiters.length() == 0) {
+			availableLineWidth = collapseSpaces(textEdit, spaceStartOffset, availableLineWidth, whitespaceRun);
+		}
+		return availableLineWidth;
+	}
+
+	private void deleteTrailingSpaces(TextEdit textEdit, ITextRegion currentTextRegion, IStructuredDocumentRegion currentDocumentRegion) {
+		int textEnd = currentTextRegion.getTextEnd();
+		int textEndOffset = currentDocumentRegion.getStartOffset() + textEnd;
+		int difference = currentTextRegion.getEnd() - textEnd;
+		DeleteEdit deleteEdit = new DeleteEdit(textEndOffset, difference);
+		textEdit.addChild(deleteEdit);
+	}
+
+	public TextEdit format(IDocument document, int start, int length) {
+		return format(document, start, length, new XMLFormattingPreferences());
+	}
+
+	public TextEdit format(IDocument document, int start, int length, XMLFormattingPreferences preferences) {
+		TextEdit edit = null;
+		if (document instanceof IStructuredDocument) {
+			IStructuredModel model = StructuredModelManager.getModelManager().getModelForEdit((IStructuredDocument) document);
+			if (model != null) {
+				try {
+					edit = format(model, start, length, preferences);
+				}
+				finally {
+					model.releaseFromEdit();
+				}
+			}
+		}
+		return edit;
+	}
+
+	public TextEdit format(IStructuredModel model, int start, int length) {
+		return format(model, start, length, new XMLFormattingPreferences());
+	}
+
+	public TextEdit format(IStructuredModel model, int start, int length, XMLFormattingPreferences preferences) {
+		setFormattingPreferences(preferences);
+
+		TextEdit edit = new MultiTextEdit();
+		IStructuredDocument document = model.getStructuredDocument();
+		// get initial document region
+		IStructuredDocumentRegion currentRegion = document.getRegionAtCharacterOffset(start);
+		if (currentRegion != null) {
+			int startOffset = currentRegion.getStartOffset();
+
+			// get initial dom node
+			IndexedRegion currentIndexedRegion = model.getIndexedRegion(startOffset);
+			if (currentIndexedRegion instanceof IDOMNode) {
+				// set up domRegion which will contain current region to be
+				// formatted
+				IDOMNode currentDOMNode = (IDOMNode) currentIndexedRegion;
+				DOMRegion domRegion = new DOMRegion();
+				domRegion.documentRegion = currentRegion;
+				domRegion.domNode = currentDOMNode;
+				
+				XMLFormattingConstraints parentConstraints = getRegionConstraints(currentDOMNode);
+				
+				/* if the whitespace strategy is declared as default, get it from the preferences */
+				if(XMLFormattingConstraints.DEFAULT.equals(parentConstraints.getWhitespaceStrategy()))
+					parentConstraints.setWhitespaceStrategy(preferences.getElementWhitespaceStrategy());
+				
+				// TODO: initialize indentLevel
+				// initialize available line width
+				int lineWidth = getFormattingPreferences().getMaxLineWidth();
+				try {
+					IRegion lineInfo = document.getLineInformationOfOffset(startOffset);
+					lineWidth = lineWidth - (startOffset - lineInfo.getOffset());
+				}
+				catch (BadLocationException e) {
+					Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
+				}
+				parentConstraints.setAvailableLineWidth(lineWidth);
+
+				// format all siblings (and their children) as long they
+				// overlap with start/length
+				Position formatRange = new Position(start, length);
+				formatSiblings(edit, domRegion, parentConstraints, formatRange);
+			}
+		}
+		return edit;
+	}
+
+	/**
+	 * Determines the formatting constraints for a specified node based on
+	 * its ancestors' formatting. In particular, if any ancestor node either
+	 * explicitly defines whitespace preservation or ignorance, that
+	 * whitespace strategy should be used for <code>currentNode</code> and 
+	 * all of its descendants.
+	 * 
+	 * @param currentNode the node to investigate the ancestry of to determine
+	 * formatting constraints
+	 * 
+	 * @return formatting constraints defined by an ancestor
+	 */
+	private XMLFormattingConstraints getRegionConstraints(IDOMNode currentNode) {
+		IDOMNode iterator = currentNode;
+		XMLFormattingConstraints result = new XMLFormattingConstraints();
+		DOMRegion region = new DOMRegion();
+		XMLFormattingConstraints parentConstraints = new XMLFormattingConstraints();
+		boolean parent = true;
+		
+		/* Iterate through the ancestry to find if any explicit whitespace strategy has
+		 * been defined
+		 */
+		while(iterator != null && iterator.getNodeType() != Node.DOCUMENT_NODE) {
+			iterator = (IDOMNode) iterator.getParentNode();
+			region.domNode = iterator;
+			region.documentRegion = iterator.getFirstStructuredDocumentRegion();
+			
+			updateFormattingConstraints(null, null, result, region);
+			
+			/* If this is the parent of the current node, keep the constraints
+			 * in case no other constraints are identified
+			 */
+			if(parent) {
+				parentConstraints.copyConstraints(result);
+				parent = false;
+			}
+			
+			/* A parent who has defined a specific whitespace strategy was found */
+			if(XMLFormattingConstraints.PRESERVE.equals(result.getWhitespaceStrategy()) || XMLFormattingConstraints.DEFAULT.equals(result.getWhitespaceStrategy()))
+				return result;
+		}
+		
+		return parentConstraints;
+	}
+//	private XMLFormattingConstraints getRegionConstraints(IDOMNode currentNode) {
+//		IDOMNode iterator = (IDOMNode) currentNode.getParentNode();
+//		XMLFormattingConstraints result = new XMLFormattingConstraints();
+//		DOMRegion region = new DOMRegion();
+//		
+//		/* Iterate through the ancestry to find if any explicit whitespace strategy has
+//		 * been defined
+//		 */
+//		while(iterator != null && iterator.getNodeType() != Node.DOCUMENT_NODE) {
+
+//			region.domNode = iterator;
+//			region.documentRegion = iterator.getFirstStructuredDocumentRegion();
+//			
+//			updateFormattingConstraints(null, null, result, region);
+//			
+//			/* A parent who has defined a specific whitespace strategy was found */
+//			if(XMLFormattingConstraints.PRESERVE == result.getWhitespaceStrategy() || XMLFormattingConstraints.DEFAULT == result.getWhitespaceStrategy())
+//				return result;
+//			
+//			iterator = (IDOMNode) iterator.getParentNode();
+//		}
+//		
+//		return null;
+//	}
+	
+	/**
+	 * Formats the given xml content region
+	 * 
+	 * @param textEdit
+	 * @param formatRange
+	 * @param parentConstraints
+	 * @param currentDOMRegion
+	 * @param previousRegion
+	 */
+	private void formatContent(TextEdit textEdit, Position formatRange, XMLFormattingConstraints parentConstraints, DOMRegion currentDOMRegion, IStructuredDocumentRegion previousRegion) {
+		IStructuredDocumentRegion currentRegion = currentDOMRegion.documentRegion;
+		String fullText = currentDOMRegion.domNode.getSource();
+
+		// check if in preserve space mode, if so, don't touch anything but
+		// make sure to update available line width
+		String whitespaceMode = parentConstraints.getWhitespaceStrategy();
+		if (XMLFormattingConstraints.PRESERVE.equals(whitespaceMode)) {
+			int availableLineWidth = parentConstraints.getAvailableLineWidth();
+			availableLineWidth = updateLineWidthWithLastLine(fullText, availableLineWidth);
+
+			// update available line width in constraints
+			parentConstraints.setAvailableLineWidth(availableLineWidth);
+			// A text node can contain multiple structured document regions - sync the documentRegion
+			// with the last region of the node since the text from all regions was formatted
+			currentDOMRegion.documentRegion = currentDOMRegion.domNode.getLastStructuredDocumentRegion();
+			return;
+		}
+
+		// if content is just whitespace and there's something after it
+		// just skip over this region because region will take care of it
+		boolean isAllWhitespace = ((IDOMText) currentDOMRegion.domNode).isElementContentWhitespace();
+		IStructuredDocumentRegion nextDocumentRegion = null;
+		if (isAllWhitespace) {
+			parentConstraints.setAvailableLineWidth(fPreferences.getMaxLineWidth());
+			nextDocumentRegion = currentRegion.getNext();
+			if (nextDocumentRegion != null)
+				return;
+		}
+
+		// special handling if text follows an entity or cdata region
+		if (!XMLFormattingConstraints.COLLAPSE.equals(whitespaceMode) && previousRegion != null) {
+			String previouRegionType = previousRegion.getType();
+			if (DOMRegionContext.XML_ENTITY_REFERENCE.equals(previouRegionType) || DOMRegionContext.XML_CDATA_TEXT.equals(previouRegionType))
+				whitespaceMode = XMLFormattingConstraints.COLLAPSE;
+		}
+		// also, special handling if text is before an entity or cdata region
+		if (!XMLFormattingConstraints.COLLAPSE.equals(whitespaceMode)) {
+			// get next document region if dont already have it
+			if (nextDocumentRegion == null)
+				nextDocumentRegion = currentRegion.getNext();
+			if (nextDocumentRegion != null) {
+				String nextRegionType = nextDocumentRegion.getType();
+				if (DOMRegionContext.XML_ENTITY_REFERENCE.equals(nextRegionType) || DOMRegionContext.XML_CDATA_TEXT.equals(nextRegionType))
+					whitespaceMode = XMLFormattingConstraints.COLLAPSE;
+			}
+		}
+		final IStructuredDocumentRegion lastRegion = currentDOMRegion.domNode.getLastStructuredDocumentRegion();
+		formatTextInContent(textEdit, parentConstraints, currentRegion, lastRegion != null ? lastRegion.getNext(): null, fullText, whitespaceMode);
+		// A text node can contain multiple structured document regions - sync the documentRegion
+		// with the last region of the node since the text from all regions was formatted
+		currentDOMRegion.documentRegion = lastRegion;
+	}
+
+	private void formatEmptyStartTagWithNoAttr(TextEdit textEdit, XMLFormattingConstraints constraints, IStructuredDocumentRegion currentDocumentRegion, IStructuredDocumentRegion previousDocumentRegion, int availableLineWidth, String indentStrategy, String whitespaceStrategy, ITextRegion currentTextRegion) {
+		// get preference if there should be a space or not between tag
+		// name and empty tag close
+		// <tagName />
+		boolean oneSpaceInTagName = getFormattingPreferences().getSpaceBeforeEmptyCloseTag();
+
+		// calculate available line width
+		int tagNameLineWidth = currentTextRegion.getTextLength() + 3;
+		if (oneSpaceInTagName) {
+			// add one more to account for space before empty tag close
+			++tagNameLineWidth;
+		}
+		availableLineWidth -= tagNameLineWidth;
+
+		if (XMLFormattingConstraints.INLINE.equals(indentStrategy)) {
+			// if was inlining, need to check if out of available line
+			// width
+			if (availableLineWidth < 0) {
+				// need to indent if possible
+				int lineWidth = indentIfPossible(textEdit, constraints, currentDocumentRegion, previousDocumentRegion, whitespaceStrategy, indentStrategy, true);
+				// update available line width
+				if (lineWidth > 0)
+					availableLineWidth = lineWidth - tagNameLineWidth;
+				else
+					availableLineWidth -= tagNameLineWidth;
+			}
+			else {
+				// no need to indent
+				// just make sure to delete previous whitespace if
+				// needed
+				if ((DOMRegionContext.XML_CONTENT.equals(previousDocumentRegion.getType())) && (previousDocumentRegion.getFullText().trim().length() == 0)) {
+					availableLineWidth = collapseSpaces(textEdit, previousDocumentRegion.getStartOffset(), availableLineWidth, previousDocumentRegion.getFullText());
+				}
+			}
+		}
+
+		// delete any trail spaces after tag name
+		int textLength = currentTextRegion.getTextLength();
+		int regionLength = currentTextRegion.getLength();
+
+		boolean thereAreSpaces = textLength < regionLength;
+		if (!oneSpaceInTagName && thereAreSpaces) {
+			deleteTrailingSpaces(textEdit, currentTextRegion, currentDocumentRegion);
+		}
+		else if(oneSpaceInTagName) {
+			insertSpaceAndCollapse(textEdit, currentDocumentRegion, availableLineWidth, currentTextRegion);
+		}
+		constraints.setAvailableLineWidth(availableLineWidth);
+	}
+
+	/**
+	 * Formats an end tag
+	 * 
+	 * @param textEdit
+	 * @param currentRegion
+	 * @param textRegions
+	 */
+	private void formatEndTag(TextEdit textEdit, Position formatRange, XMLFormattingConstraints constraints, DOMRegion currentDOMRegion, IStructuredDocumentRegion previousDocumentRegion) {
+		IStructuredDocumentRegion currentDocumentRegion = currentDOMRegion.documentRegion;
+
+		String whitespaceStrategy = constraints.getWhitespaceStrategy();
+		String indentStrategy = constraints.getIndentStrategy();
+
+		// do not format space before start tag if preserving spaces
+		if (whitespaceStrategy != XMLFormattingConstraints.PRESERVE) {
+			// format like indent strategy says
+			if (XMLFormattingConstraints.INDENT.equals(indentStrategy) || XMLFormattingConstraints.NEW_LINE.equals(indentStrategy)) {
+				int availableLineWidth = indentIfPossible(textEdit, constraints, currentDocumentRegion, previousDocumentRegion, whitespaceStrategy, indentStrategy, false);
+				constraints.setAvailableLineWidth(availableLineWidth);
+			}
+			else if ( XMLFormattingConstraints.INLINE.equals(indentStrategy)){
+				IStructuredDocument doc = currentDocumentRegion.getParentDocument();
+				int currentLine = doc.getLineOfOffset(currentDocumentRegion.getStartOffset());
+				int prevLine = doc.getLineOfOffset(previousDocumentRegion.getStartOffset());
+				if ( currentLine != prevLine){
+					int availableLineWidth = indentIfPossible(textEdit, constraints, currentDocumentRegion, previousDocumentRegion, whitespaceStrategy, indentStrategy, false);
+					constraints.setAvailableLineWidth(availableLineWidth);
+				}
+			}
+		}
+		// format the end tag itself
+		formatWithinEndTag(textEdit, constraints, currentDocumentRegion, previousDocumentRegion);
+	}
+
+	/**
+	 * Formats the given region (and all its children) contained in domRegion.
+	 * 
+	 * @param edit
+	 *            edits required to format
+	 * @param formatRange
+	 *            document range to format (only format content within this
+	 *            range)
+	 * @param parentConstraints
+	 * @param domRegion
+	 *            assumes dom node & region are not null
+	 * @param previousRegion
+	 *            could be null
+	 * @return Returns the last region formatted
+	 */
+	private DOMRegion formatRegion(TextEdit edit, Position formatRange, XMLFormattingConstraints parentConstraints, DOMRegion domRegion, IStructuredDocumentRegion previousRegion) {
+		IStructuredDocumentRegion currentRegion = domRegion.documentRegion;
+		String regionType = currentRegion.getType();
+		if (DOMRegionContext.XML_TAG_NAME.equals(regionType)) {
+			ITextRegion textRegion = currentRegion.getFirstRegion();
+			String textRegionType = textRegion.getType();
+			if (DOMRegionContext.XML_TAG_OPEN.equals(textRegionType)) {
+				domRegion = formatStartTag(edit, formatRange, parentConstraints, domRegion, previousRegion);
+			}
+			else if (DOMRegionContext.XML_END_TAG_OPEN.equals(textRegionType)) {
+				formatEndTag(edit, formatRange, parentConstraints, domRegion, previousRegion);
+			}
+		}
+		else if (DOMRegionContext.XML_CONTENT.equals(regionType) || domRegion.domNode.getNodeType() == Node.TEXT_NODE) {
+			formatContent(edit, formatRange, parentConstraints, domRegion, previousRegion);
+		}
+		else if (DOMRegionContext.XML_COMMENT_TEXT.equals(regionType)) {
+			formatComment(edit, formatRange, parentConstraints, domRegion, previousRegion);
+		}
+		else {
+			// unknown, so just leave alone for now but make sure to update
+			// available line width
+			String fullText = currentRegion.getFullText();
+			int width = updateLineWidthWithLastLine(fullText, parentConstraints.getAvailableLineWidth());
+			parentConstraints.setAvailableLineWidth(width);
+		}
+		return domRegion;
+	}
+
+	/**
+	 * Formats the domRegion and all of its children and siblings
+	 * 
+	 * @param edit
+	 * @param domRegion
+	 * @param parentConstraints
+	 * @param formatRange
+	 */
+	private void formatSiblings(TextEdit edit, DOMRegion domRegion, XMLFormattingConstraints parentConstraints, Position formatRange) {
+		IStructuredDocumentRegion previousRegion = null;
+		IStructuredDocumentRegion currentRegion = domRegion.documentRegion;
+		IDOMNode currentDOMNode = domRegion.domNode;
+		while (currentDOMNode != null && currentRegion != null && formatRange.overlapsWith(currentRegion.getStartOffset(), currentRegion.getLength()) && (fProgressMonitor == null || !fProgressMonitor.isCanceled())) {
+			domRegion.documentRegion = currentRegion;
+			domRegion.domNode = currentDOMNode;
+
+			// need to make sure current document region and current
+			// dom node match up
+			if (currentDOMNode.getFirstStructuredDocumentRegion().equals(currentRegion)) {
+				// format this document region/node, formatRegion will
+				// return the last node/region formatted
+				domRegion = formatRegion(edit, formatRange, parentConstraints, domRegion, previousRegion);
+			}
+			else {
+				// TODO: need to figure out what to do if they don't
+				// match up
+			}
+			previousRegion = domRegion.documentRegion;
+			// get the next sibling information
+			if (domRegion.domNode != null)
+				currentDOMNode = (IDOMNode) domRegion.domNode.getNextSibling();
+			else
+				currentDOMNode = null;
+			currentRegion = previousRegion.getNext();
+		}
+	}
+
+	/**
+	 * Formats a start tag
+	 * 
+	 * @param textEdit
+	 * @param currentRegion
+	 * @param textRegions
+	 */
+	private DOMRegion formatStartTag(TextEdit textEdit, Position formatRange, XMLFormattingConstraints parentConstraints, DOMRegion currentDOMRegion, IStructuredDocumentRegion previousDocumentRegion) {
+		// determine proper indent by referring to parent constraints,
+		// previous node, and current node
+		IStructuredDocumentRegion currentDocumentRegion = currentDOMRegion.documentRegion;
+		IDOMNode currentDOMNode = currentDOMRegion.domNode;
+
+		// create a constraint for this tag
+		XMLFormattingConstraints thisConstraints = new XMLFormattingConstraints();
+		XMLFormattingConstraints childrenConstraints = new XMLFormattingConstraints();
+		updateFormattingConstraints(parentConstraints, thisConstraints, childrenConstraints, currentDOMRegion);
+
+		if(XMLFormattingConstraints.DEFAULT.equals(childrenConstraints.getWhitespaceStrategy()))
+			childrenConstraints.setWhitespaceStrategy((new XMLFormattingPreferences()).getElementWhitespaceStrategy());
+			
+		String whitespaceStrategy = thisConstraints.getWhitespaceStrategy();
+		String indentStrategy = thisConstraints.getIndentStrategy();
+		int availableLineWidth = thisConstraints.getAvailableLineWidth();
+
+		// format space before start tag
+		// do not format space before start tag if preserving spaces
+		if (!XMLFormattingConstraints.PRESERVE.equals(whitespaceStrategy)) {
+			// format like indent strategy says
+			if (XMLFormattingConstraints.INDENT.equals(indentStrategy) || XMLFormattingConstraints.NEW_LINE.equals(indentStrategy)) {
+				availableLineWidth = indentIfPossible(textEdit, thisConstraints, currentDocumentRegion, previousDocumentRegion, whitespaceStrategy, indentStrategy, true, true);
+				if (availableLineWidth > 0)
+					thisConstraints.setAvailableLineWidth(availableLineWidth);
+			}
+		}
+		// format the start tag itself
+		boolean tagEnded = formatWithinTag(textEdit, thisConstraints, currentDocumentRegion, previousDocumentRegion);
+
+		// format children
+		if (!tagEnded) {
+			// update childConstraints with thisConstraint's indentLevel &
+			// availableLineWidth
+			childrenConstraints.setIndentLevel(thisConstraints.getIndentLevel());
+			childrenConstraints.setAvailableLineWidth(thisConstraints.getAvailableLineWidth());
+
+			previousDocumentRegion = currentDocumentRegion;
+			IDOMNode childDOMNode = (IDOMNode) currentDOMNode.getFirstChild();
+			IStructuredDocumentRegion nextRegion = currentDocumentRegion.getNext();
+			boolean passedFormatRange = false;
+			// as long as there is one child
+			if (childDOMNode != null && nextRegion != null) {
+				while (childDOMNode != null && nextRegion != null && !passedFormatRange && (fProgressMonitor == null || !fProgressMonitor.isCanceled())) {
+					DOMRegion childDOMRegion = new DOMRegion();
+					childDOMRegion.documentRegion = nextRegion;
+					childDOMRegion.domNode = childDOMNode;
+					if (nextRegion.equals(childDOMNode.getFirstStructuredDocumentRegion())) {
+						// format children. pass in child constraints
+						childDOMRegion = formatRegion(textEdit, formatRange, childrenConstraints, childDOMRegion, previousDocumentRegion);
+					}
+					else {
+						// TODO: what happens if they dont match up?
+					}
+
+					// update childDOMRegion with next dom/region node
+					if (childDOMRegion.domNode != null) {
+						childDOMNode = (IDOMNode) childDOMRegion.domNode.getNextSibling();
+					}
+					else {
+						childDOMNode = null;
+					}
+					previousDocumentRegion = childDOMRegion.documentRegion;
+					nextRegion = previousDocumentRegion.getNext();
+					if (nextRegion != null)
+						passedFormatRange = !formatRange.overlapsWith(nextRegion.getStartOffset(), nextRegion.getLength());
+				}
+			}
+			else {
+				// there were no children, so keep end tag inlined
+				childrenConstraints.setWhitespaceStrategy(XMLFormattingConstraints.COLLAPSE);
+				childrenConstraints.setIndentStrategy(XMLFormattingConstraints.INLINE);
+			}
+
+			if (!passedFormatRange) {
+				// update the dom region with the last formatted region/dom
+				// node should be end tag and this tag's DOMNode
+				currentDOMRegion.documentRegion = nextRegion;
+				currentDOMRegion.domNode = currentDOMNode;
+
+				// end tag's indent level should be same as start tag's
+				childrenConstraints.setIndentLevel(thisConstraints.getIndentLevel());
+				// format end tag
+				boolean formatEndTag = false;
+				if (nextRegion != null && currentDOMNode != null) {
+					ITextRegionList rs = nextRegion.getRegions();
+					if (rs.size() > 1) {
+						ITextRegion r = rs.get(0);
+						if (r != null && DOMRegionContext.XML_END_TAG_OPEN.equals(r.getType())) {
+							r = rs.get(1);
+							if (r != null && DOMRegionContext.XML_TAG_NAME.equals(r.getType())) {
+								String tagName = nextRegion.getText(r);
+								if (tagName != null && tagName.equals(currentDOMNode.getNodeName()))
+									formatEndTag = true;
+							}
+						}
+
+					}
+				}
+				if (formatEndTag)
+					formatEndTag(textEdit, formatRange, childrenConstraints, currentDOMRegion, previousDocumentRegion);
+				else {
+					// missing end tag so return last formatted document
+					// region
+					currentDOMRegion.documentRegion = previousDocumentRegion;
+				}
+			}
+			else {
+				// passed format range before could finish, so update dom
+				// region to last known formatted region
+				currentDOMRegion.documentRegion = nextRegion;
+				currentDOMRegion.domNode = childDOMNode;
+			}
+
+			// update parent constraint since this is what is passed back
+			parentConstraints.setAvailableLineWidth(childrenConstraints.getAvailableLineWidth());
+		}
+		else {
+			// update available line width
+			parentConstraints.setAvailableLineWidth(thisConstraints.getAvailableLineWidth());
+		}
+		return currentDOMRegion;
+	}
+
+	private void formatStartTagWithNoAttr(TextEdit textEdit, XMLFormattingConstraints constraints, IStructuredDocumentRegion currentDocumentRegion, IStructuredDocumentRegion previousDocumentRegion, int availableLineWidth, String indentStrategy, String whitespaceStrategy, ITextRegion currentTextRegion) {
+		// calculate available line width
+		int tagNameLineWidth = currentTextRegion.getTextLength() + 2;
+		availableLineWidth -= tagNameLineWidth;
+
+		if (XMLFormattingConstraints.INLINE.equals(indentStrategy)) {
+			// if was inlining, need to check if out of available line
+			// width
+			if (availableLineWidth < 0) {
+				// need to indent if possible
+				int lineWidth = indentIfPossible(textEdit, constraints, currentDocumentRegion, previousDocumentRegion, whitespaceStrategy, indentStrategy, true);
+				// update available line width
+				if (lineWidth > 0)
+					availableLineWidth = lineWidth - tagNameLineWidth;
+				else
+					availableLineWidth -= tagNameLineWidth;
+			}
+			else {
+				// no need to indent
+				// just make sure to delete previous whitespace if
+				// needed
+				if (previousDocumentRegion != null) {
+					if (DOMRegionContext.XML_CONTENT.equals(previousDocumentRegion.getType())) {
+						String previousDocumentRegionText = previousDocumentRegion.getFullText();
+						if (previousDocumentRegionText.trim().length() == 0) {
+							availableLineWidth = collapseSpaces(textEdit, previousDocumentRegion.getStartOffset(), availableLineWidth, previousDocumentRegionText);
+						}
+					}
+				}
+			}
+		}
+
+		// delete any trail spaces after tag name
+		if (currentTextRegion.getTextLength() < currentTextRegion.getLength()) {
+			deleteTrailingSpaces(textEdit, currentTextRegion, currentDocumentRegion);
+		}
+		constraints.setAvailableLineWidth(availableLineWidth);
+	}
+
+	/**
+	 * Format the text in xml content
+	 * 
+	 * @param textEdit
+	 * @param parentConstraints
+	 * @param currentRegion
+	 * @param fullText
+	 * @param whitespaceMode
+	 */
+	private void formatTextInContent(TextEdit textEdit, XMLFormattingConstraints parentConstraints, IStructuredDocumentRegion currentRegion, IStructuredDocumentRegion nextRegion, String fullText, String whitespaceMode) {
+		int availableLineWidth = parentConstraints.getAvailableLineWidth();
+
+		// determine indentation
+		boolean forceInitialIndent = false;
+		int indentLevel = parentConstraints.getIndentLevel() + 1;
+		String indentMode = parentConstraints.getIndentStrategy();
+		if (XMLFormattingConstraints.INDENT.equals(indentMode)) {
+			forceInitialIndent = true;
+		}
+		if (XMLFormattingConstraints.NEW_LINE.equals(indentMode)) {
+			indentLevel = parentConstraints.getIndentLevel();
+			forceInitialIndent = true;
+		}
+
+		int fullTextOffset = 0;
+		char[] fullTextArray = fullText.toCharArray();
+		while (fullTextOffset < fullTextArray.length) {
+			// gather all whitespaces
+			String whitespaceRun = getCharacterRun(fullTextArray, fullTextOffset, true);
+			if (whitespaceRun.length() > 0) {
+				// offset where whitespace starts
+				int whitespaceStart = fullTextOffset;
+				// update current offset in fullText
+				fullTextOffset += whitespaceRun.length();
+
+				// gather following word
+				String characterRun = getCharacterRun(fullTextArray, fullTextOffset, false);
+				int characterRunLength = characterRun.length();
+				if (characterRunLength > 0) {
+					// indent if word is too long or forcing initial
+					// indent
+					availableLineWidth -= characterRunLength;
+					// offset where indent/collapse will happen
+					int startOffset = currentRegion.getStartOffset() + whitespaceStart;
+					if (forceInitialIndent || (availableLineWidth <= 0)) {
+						// indent if not already indented
+						availableLineWidth = indentIfNotAlreadyIndented(textEdit, currentRegion, indentLevel, startOffset, whitespaceRun);
+						// remember to subtract word length
+						availableLineWidth -= characterRunLength;
+						forceInitialIndent = false; // initial indent done
+					}
+					else {
+						// just collapse spaces, but adjust for any indenting that may result from preserving line delimiters
+						if (whitespaceStart == 0 && XMLFormattingConstraints.IGNOREANDTRIM.equals(whitespaceMode)) {
+							// if ignore, trim
+							DeleteEdit deleteTrailing = new DeleteEdit(startOffset, whitespaceRun.length());
+							textEdit.addChild(deleteTrailing);
+						}
+						else if(XMLFormattingConstraints.REPLACE.equals(whitespaceMode))
+							availableLineWidth = replaceSpaces(textEdit, startOffset, availableLineWidth, whitespaceRun);
+						else
+							availableLineWidth = collapseAndIndent(textEdit, startOffset, availableLineWidth, indentLevel, whitespaceRun, currentRegion);
+					}
+
+					fullTextOffset += characterRunLength;
+				}
+				else {
+					// handle trailing whitespace
+					int whitespaceOffset = currentRegion.getStartOffset() + whitespaceStart;
+					if (XMLFormattingConstraints.REPLACE.equals(whitespaceMode))
+						availableLineWidth = replaceSpaces(textEdit, whitespaceOffset, availableLineWidth, whitespaceRun);
+					else if (XMLFormattingConstraints.IGNOREANDTRIM.equals(whitespaceMode)) {
+						// always trim
+						DeleteEdit deleteTrailing = new DeleteEdit(whitespaceOffset, whitespaceRun.length());
+						textEdit.addChild(deleteTrailing);
+					}
+					else if(getFormattingPreferences().getClearAllBlankLines()) {
+						if (!nextRegionHandlesTrailingWhitespace(nextRegion)) {
+							if (XMLFormattingConstraints.IGNORE.equals(whitespaceMode)) {
+								// if ignore, trim
+								DeleteEdit deleteTrailing = new DeleteEdit(whitespaceOffset, whitespaceRun.length());
+								textEdit.addChild(deleteTrailing);
+							}
+							else {
+								// if collapse, leave a space. but what if end up
+								// wanting to add indent? then need to delete space
+								// added and add indent instead
+								availableLineWidth = collapseSpaces(textEdit, whitespaceOffset, availableLineWidth, whitespaceRun);
+							}
+						}
+					}
+				}
+			}
+			else {
+				// gather word
+				String characterRun = getCharacterRun(fullTextArray, fullTextOffset, false);
+				int characterRunLength = characterRun.length();
+				if (characterRunLength > 0) {
+					// indent if word is too long or forcing initial
+					// indent
+					// [243091] - characterRunLength should only be subtracted once or text formatting wraps prematurely
+					// availableLineWidth = availableLineWidth - characterRunLength;
+					if ((XMLFormattingConstraints.IGNORE.equals(whitespaceMode) || XMLFormattingConstraints.IGNOREANDTRIM.equals(whitespaceMode)) && (forceInitialIndent || (availableLineWidth <= 0))) {
+						// indent if not already indented
+						availableLineWidth = indentIfNotAlreadyIndented(textEdit, currentRegion, indentLevel, currentRegion.getStartOffset(), whitespaceRun);
+						// remember to subtract word length
+						availableLineWidth -= characterRunLength;
+						forceInitialIndent = false; // initial indent done
+					}
+					else {
+						// just collapse spaces
+						availableLineWidth -= characterRunLength;
+					}
+
+					fullTextOffset += characterRunLength;
+				}
+			}
+		}
+		// update available line width
+		parentConstraints.setAvailableLineWidth(availableLineWidth);
+	}
+
+	private boolean nextRegionHandlesTrailingWhitespace(IStructuredDocumentRegion region) {
+		if (region == null)
+			return false;
+		final String type = region.getType();
+		if (type.equals(DOMRegionContext.XML_TAG_NAME)) {
+			return DOMRegionContext.XML_TAG_OPEN.equals(region.getFirstRegion().getType());
+		}
+		return DOMRegionContext.XML_COMMENT_TEXT.equals(type);
+	}
+
+	private void formatWithinEndTag(TextEdit textEdit, XMLFormattingConstraints constraints, IStructuredDocumentRegion currentDocumentRegion, IStructuredDocumentRegion previousDocumentRegion) {
+		String indentStrategy = constraints.getIndentStrategy();
+		String whitespaceStrategy = constraints.getWhitespaceStrategy();
+		int availableLineWidth = constraints.getAvailableLineWidth();
+		ITextRegionList textRegions = currentDocumentRegion.getRegions();
+		int currentNumberOfRegions = currentDocumentRegion.getNumberOfRegions();
+		int currentTextRegionIndex = 1;
+
+		ITextRegion currentTextRegion = textRegions.get(currentTextRegionIndex);
+		String currentType = currentTextRegion.getType();
+		// tag name should always be the first text region
+		if (DOMRegionContext.XML_TAG_NAME.equals(currentType) && currentTextRegionIndex < currentNumberOfRegions - 1) {
+			ITextRegion nextTextRegion = textRegions.get(currentTextRegionIndex + 1);
+			// Bug 221279 - Some non well-formed documents will not contribute a next region
+			if (nextTextRegion != null && DOMRegionContext.XML_TAG_CLOSE.equals(nextTextRegion.getType())) {
+				// calculate available line width
+				int tagNameLineWidth = currentTextRegion.getTextLength() + 3;
+				availableLineWidth -= tagNameLineWidth;
+
+				if (XMLFormattingConstraints.INLINE.equals(indentStrategy)) {
+					// if was inlining, need to check if out of available line
+					// width - Whitespace may have been corrected in the text content
+					if (availableLineWidth < 0 && XMLFormattingConstraints.IGNORE.equals(whitespaceStrategy)) {
+						// need to deindent if possible
+						int lineWidth = indentIfPossible(textEdit, constraints, currentDocumentRegion, previousDocumentRegion, whitespaceStrategy, indentStrategy, false);
+						// update available line width
+						if (lineWidth > 0)
+							availableLineWidth = lineWidth - tagNameLineWidth;
+					}
+					else {
+						// no need to indent
+						// just make sure to delete previous whitespace if
+						// needed
+						if (previousDocumentRegion != null) {
+							if (DOMRegionContext.XML_CONTENT.equals(previousDocumentRegion.getType())) {
+								String previousDocumentRegionText = previousDocumentRegion.getFullText();
+								if (previousDocumentRegionText.trim().length() == 0) {
+									availableLineWidth = collapseSpaces(textEdit, previousDocumentRegion.getStartOffset(), availableLineWidth, previousDocumentRegionText);
+								}
+							}
+						}
+					}
+				}
+				// delete any trail spaces after tag name
+				if (currentTextRegion.getTextLength() < currentTextRegion.getLength()) {
+					deleteTrailingSpaces(textEdit, currentTextRegion, currentDocumentRegion);
+				}
+			}
+		}
+		else {
+			// end tag has unexpected stuff, so just leave it alone
+		}
+		constraints.setAvailableLineWidth(availableLineWidth);
+	}
+
+	/**
+	 * Formats the contents within a tag like tag name and attributes
+	 * 
+	 * @param textEdit
+	 * @param currentDocumentRegion
+	 * @param textRegions
+	 *            contains at least 3 regions
+	 * @return true if tag was ended, false otherwise
+	 */
+	private boolean formatWithinTag(TextEdit textEdit, XMLFormattingConstraints constraints, IStructuredDocumentRegion currentDocumentRegion, IStructuredDocumentRegion previousDocumentRegion) {
+		int availableLineWidth = constraints.getAvailableLineWidth();
+		String indentStrategy = constraints.getIndentStrategy();
+		String whitespaceStrategy = constraints.getWhitespaceStrategy();
+		int indentLevel = constraints.getIndentLevel();
+		ITextRegionList textRegions = currentDocumentRegion.getRegions();
+		int currentTextRegionIndex = 1;
+
+		ITextRegion currentTextRegion = textRegions.get(currentTextRegionIndex);
+		String currentType = currentTextRegion.getType();
+		// tag name should always be the first text region
+		if (DOMRegionContext.XML_TAG_NAME.equals(currentType)) {
+			ITextRegion nextTextRegion = textRegions.get(currentTextRegionIndex + 1);
+			String nextType = (nextTextRegion != null) ? nextTextRegion.getType() : null;
+			if (DOMRegionContext.XML_TAG_CLOSE.equals(nextType)) {
+				// already at tag close
+				formatStartTagWithNoAttr(textEdit, constraints, currentDocumentRegion, previousDocumentRegion, availableLineWidth, indentStrategy, whitespaceStrategy, currentTextRegion);
+				return false;
+			}
+			else if (DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(nextType)) {
+				// already at empty tag close
+				formatEmptyStartTagWithNoAttr(textEdit, constraints, currentDocumentRegion, previousDocumentRegion, availableLineWidth, indentStrategy, whitespaceStrategy, currentTextRegion);
+				return true;
+			}
+			else {
+				availableLineWidth -= (currentTextRegion.getTextLength() + 2);
+				boolean alignFinalBracket = getFormattingPreferences().getAlignFinalBracket();
+				boolean oneSpaceInTagName = getFormattingPreferences().getSpaceBeforeEmptyCloseTag();
+				boolean indentMultipleAttribute = getFormattingPreferences().getIndentMultipleAttributes();
+				// indicates if tag spanned more than one line
+				boolean spanMoreThan1Line = false;
+				// indicates if all attributes should be indented
+				boolean indentAllAttributes = false;
+				if (indentMultipleAttribute) {
+					int attributesCount = 0;
+					int i = 2;
+					final int size = textRegions.size();
+					while (i < size && attributesCount < 2) {
+						if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(textRegions.get(i).getType())) {
+							++attributesCount;
+						}
+						i++;
+					}
+					indentAllAttributes = (attributesCount > 1);
+				}
+
+				while ((currentTextRegionIndex + 1) < textRegions.size()) {
+					nextTextRegion = textRegions.get(currentTextRegionIndex + 1);
+					nextType = nextTextRegion.getType();
+					if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(nextType)) {
+						boolean indentAttribute = indentAllAttributes;
+						if (!indentAttribute)
+							indentAttribute = shouldIndentBeforeAttribute(constraints, textRegions, availableLineWidth, currentTextRegionIndex, currentTextRegion, nextTextRegion);
+						if (indentAttribute) {
+							availableLineWidth = indentIfNotAlreadyIndented(textEdit, indentLevel + 1, currentDocumentRegion, currentTextRegion);
+							spanMoreThan1Line = true;
+						}
+						else {
+							// otherwise, insertSpaceAndCollapse
+							insertSpaceAndCollapse(textEdit, currentDocumentRegion, availableLineWidth, currentTextRegion);
+							// update available line width
+							availableLineWidth -= (currentTextRegion.getTextLength() + 1);
+						}
+					}
+					else if (DOMRegionContext.XML_TAG_CLOSE.equals(nextType)) {
+						// if need to align bracket on next line, indent
+						if (alignFinalBracket && spanMoreThan1Line) {
+							availableLineWidth = indentIfNotAlreadyIndented(textEdit, indentLevel, currentDocumentRegion, currentTextRegion);
+							--availableLineWidth; // for tag close itself
+						}
+						else {
+							// otherwise, just delete space before tag close
+							if (currentTextRegion.getTextLength() < currentTextRegion.getLength()) {
+								deleteTrailingSpaces(textEdit, currentTextRegion, currentDocumentRegion);
+								availableLineWidth -= (currentTextRegion.getTextLength() + 1);
+							}
+						}
+						// update line width
+						constraints.setAvailableLineWidth(availableLineWidth);
+						return false;
+					}
+					else if (DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(nextType)) {
+						int textLength = currentTextRegion.getTextLength();
+						int regionLength = currentTextRegion.getLength();
+
+						boolean thereAreSpaces = textLength < regionLength;
+						if (!oneSpaceInTagName && thereAreSpaces) {
+							// delete any trail spaces after tag name
+							deleteTrailingSpaces(textEdit, currentTextRegion, currentDocumentRegion);
+							availableLineWidth -= (currentTextRegion.getTextLength() + 2);
+						}
+						// insert a space and collapse ONLY IF it's specified
+						else if (oneSpaceInTagName) {
+							insertSpaceAndCollapse(textEdit, currentDocumentRegion, availableLineWidth, currentTextRegion);
+							availableLineWidth -= (currentTextRegion.getTextLength() + 3);
+						}
+						// update line width
+						constraints.setAvailableLineWidth(availableLineWidth);
+						return true;
+					}
+					else {
+						if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(currentType) && DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS.equals(nextType)) {
+							if (currentTextRegion.getTextLength() < currentTextRegion.getLength()) {
+								deleteTrailingSpaces(textEdit, currentTextRegion, currentDocumentRegion);
+							}
+							// update available width
+							availableLineWidth -= currentTextRegion.getTextLength();
+						}
+						else if (DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS.equals(currentType) && DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(nextType)) {
+							if (currentTextRegion.getTextLength() < currentTextRegion.getLength()) {
+								deleteTrailingSpaces(textEdit, currentTextRegion, currentDocumentRegion);
+							}
+							// update available width
+							availableLineWidth -= currentTextRegion.getTextLength();
+						}
+						else {
+							// otherwise, insertSpaceAndCollapse
+							insertSpaceAndCollapse(textEdit, currentDocumentRegion, availableLineWidth, currentTextRegion);
+							// update available line width
+							availableLineWidth -= (currentTextRegion.getTextLength() + 1);
+						}
+					}
+					currentTextRegion = nextTextRegion;
+					currentType = nextType;
+					++currentTextRegionIndex;
+				}
+			}
+		}
+		// update line width
+		constraints.setAvailableLineWidth(availableLineWidth);
+		return false;
+	}
+	
+	/**
+	 * Format an XML comment structured document region.
+	 */
+	private void formatComment(TextEdit textEdit, Position formatRange, XMLFormattingConstraints parentConstraints, DOMRegion currentDOMRegion, IStructuredDocumentRegion previousRegion) {
+		IStructuredDocumentRegion currentRegion = currentDOMRegion.documentRegion;
+		int lineWidth = parentConstraints.getAvailableLineWidth() - currentRegion.getFullText().length();
+		// Don't format if we're not exceeding the available line width, or if the whitespace
+		// strategy is to preserve whitespace - But update line width.
+		if(currentRegion == null ||	XMLFormattingConstraints.PRESERVE.equals(parentConstraints.getWhitespaceStrategy()) || !fPreferences.getFormatCommentText()) {
+			parentConstraints.setAvailableLineWidth(lineWidth);
+			return;
+		}
+
+		Iterator it = currentRegion.getRegions().iterator();
+		ITextRegion previous = null;
+		if (previousRegion == null)
+			previousRegion = currentRegion.getPrevious();
+		// Iterate over each text region of the comment
+		Node parent = currentDOMRegion.domNode.getParentNode();
+		while(it.hasNext()) {
+			ITextRegion text = (ITextRegion) it.next();
+			String type = text.getType();
+			if (type == DOMRegionContext.XML_COMMENT_OPEN) {
+				int indentLevel = (parent != null && parent.getNodeType() == Node.DOCUMENT_NODE) ? 0 : 1;
+				int width = formatCommentStart(textEdit, parentConstraints, indentLevel, currentRegion, previousRegion, text);
+				parentConstraints.setAvailableLineWidth(width);
+			}
+			else if (type == DOMRegionContext.XML_COMMENT_TEXT) {
+				int indentLevel = (parent != null && parent.getNodeType() == Node.DOCUMENT_NODE) ? -1 : parentConstraints.getIndentLevel();
+				formatCommentContent(textEdit, parentConstraints, indentLevel, currentRegion, previous, text);
+			}
+			previous = text;
+		}
+	}
+
+	private void formatCommentContent(TextEdit textEdit, XMLFormattingConstraints parentConstraints, int indentLevel, IStructuredDocumentRegion currentRegion, ITextRegion previous, ITextRegion region) {
+		int lineWidth = parentConstraints.getAvailableLineWidth() - currentRegion.getFullText(previous).length();
+		// If there's more text than line width available, format
+		String text = currentRegion.getFullText(region);
+		compressContent(textEdit, currentRegion, currentRegion.getStartOffset(region), indentLevel + 1, lineWidth, text);
+	}
+
+	private void compressContent(TextEdit textEdit, IStructuredDocumentRegion region, int startOffset, int indentLevel, int lineWidth, String text) {
+		int length = text.length();
+		int start = 0, end = 0;
+		char c = 0;
+		int resultLength = 0;
+		boolean joinLines = fPreferences.getJoinCommentLines();
+		boolean onOwnLine = false;
+
+		String indent = getIndentString(indentLevel + 1);
+
+		for (int i = 0; i < length; i++) {
+			c = text.charAt(i);
+			// Compress whitespace unless its a line delimiter and formatting does not permit joining lines
+			if (Character.isWhitespace(c)) {
+				if ((c != '\r' && c!= '\n') || joinLines) {
+					// Just came off of a word
+					if (start == end) {
+						start = end = i;
+					}
+					end++;
+					resultLength++;
+				}
+				else {
+					// correct the indent of this line
+					lineWidth = fPreferences.getMaxLineWidth();
+					resultLength = 0;
+					onOwnLine = true;
+
+					// Compress any whitespace before the line delimiter
+					if (start != end) {
+						int replaceLength = end - start;
+						textEdit.addChild(new ReplaceEdit(start + startOffset, replaceLength, EMPTY));
+						start = end = i;
+					}
+				}
+			}
+			else {
+				// Transitioned to a new word
+				if (start != end) {
+					int replaceLength = end - start;
+					if (onOwnLine) {
+						// If content is on its own line, replace leading whitespace with proper indent
+						textEdit.addChild(new ReplaceEdit(start + startOffset, replaceLength, indent));
+						resultLength -= (replaceLength - indent.length());
+						onOwnLine = false;
+					}
+					else if (!(replaceLength == 1 && text.charAt(start) == ' ')) {
+						textEdit.addChild(new ReplaceEdit(start + startOffset, replaceLength, SPACE));
+						resultLength -= (replaceLength - 1);
+					}
+					start = end = i;
+					// Make sure the word starts on a new line
+					if (resultLength > lineWidth) {
+						lineWidth = fPreferences.getMaxLineWidth();
+						resultLength = 0;
+						textEdit.addChild(new InsertEdit(start + startOffset, getLineDelimiter(region) + indent));
+					}
+				}
+				// Word is immediately after line delimiters, indent appropriately
+				if (onOwnLine) {
+					textEdit.addChild(new InsertEdit(i + startOffset, indent));
+					onOwnLine = false;
+				}
+				resultLength++;
+			}
+		}
+
+		// Clean up any dangling whitespace
+		int replaceLength = end - start;
+		indent = getIndentString(indentLevel);
+		if (replaceLength == 0) { // No trailing whitespace
+			textEdit.addChild(new InsertEdit(length + startOffset, (onOwnLine) ? indent : SPACE));
+		}
+		else {
+			String whitespace = text.substring(start);
+			String replacement = (onOwnLine) ? indent : SPACE;
+			if (!whitespace.equals(replacement)) {
+				textEdit.addChild(new ReplaceEdit(start + startOffset, replaceLength, replacement));
+			}
+		}
+	}
+
+	private int formatCommentStart(TextEdit textEdit, XMLFormattingConstraints parentConstraints, int indentLevel, IStructuredDocumentRegion currentRegion, IStructuredDocumentRegion previousRegion, ITextRegion region) {
+		int lineWidth = parentConstraints.getAvailableLineWidth();
+		if (previousRegion!=null&&(DOMRegionContext.XML_CONTENT.equals(previousRegion.getType()))) {
+			String previousText = previousRegion.getFullText();
+			String trailingWhitespace = getTrailingWhitespace(previousText);
+			String delimiters = extractLineDelimiters(trailingWhitespace, previousRegion);
+			if (delimiters != null && delimiters.length() > 0){// && previousText.length() == trailingWhitespace.length()) {
+				// Format the comment if it's on a new line
+				int offset = previousRegion.getEnd() - trailingWhitespace.length();
+				lineWidth = indentIfNotAlreadyIndented(textEdit, currentRegion, parentConstraints.getIndentLevel() + indentLevel, offset, trailingWhitespace);
+			}
+		}
+		return lineWidth;
+	}
+
+	/**
+	 * Returns either a String of whitespace or characters depending on
+	 * forWhitespace
+	 * 
+	 * @param fullTextArray
+	 *            the text array to look in
+	 * @param textOffset
+	 *            the start offset to start searching
+	 * @param forWhitespace
+	 *            true if should return whitespaces, false otherwise
+	 * @return a String of either all whitespace or all characters. Never
+	 *         returns null
+	 */
+	private String getCharacterRun(char[] fullTextArray, int textOffset, boolean forWhitespace) {
+		StringBuffer characterRun = new StringBuffer();
+		boolean nonCharacterFound = false;
+		while (textOffset < fullTextArray.length && !nonCharacterFound) {
+			char c = fullTextArray[textOffset];
+			boolean isWhitespace = Character.isWhitespace(c);
+			if ((forWhitespace && isWhitespace) || (!forWhitespace && !isWhitespace))
+				characterRun.append(c);
+			else
+				nonCharacterFound = true;
+			++textOffset;
+		}
+		return characterRun.toString();
+	}
+	
+	private String getTrailingWhitespace(String text) {
+		StringBuffer whitespaceRun = new StringBuffer();
+		int index = text.length() - 1;
+		while(index >= 0) {
+			char c = text.charAt(index--);
+			if (Character.isWhitespace(c))
+				whitespaceRun.insert(0, c);
+			else
+				break;
+		}
+		return whitespaceRun.toString();
+	}
+
+	private String getIndentString(int indentLevel) {
+		StringBuffer indentString = new StringBuffer();
+		String indent = getFormattingPreferences().getOneIndent();
+		for (int i = 0; i < indentLevel; ++i) {
+			indentString.append(indent);
+		}
+		return indentString.toString();
+	}
+
+	protected XMLFormattingPreferences getFormattingPreferences() {
+		if (fPreferences == null)
+			fPreferences = new XMLFormattingPreferences();
+		return fPreferences;
+	}
+
+	protected void setFormattingPreferences(XMLFormattingPreferences preferences) {
+		fPreferences = preferences;
+	}
+
+	/**
+	 * Indent if whitespaceRun does not already contain an indent
+	 * 
+	 * @param textEdit
+	 * @param indentLevel
+	 * @param indentStartOffset
+	 * @param maxAvailableLineWidth
+	 * @param whitespaceRun
+	 * @return new available line width up to where indented
+	 */
+	private int indentIfNotAlreadyIndented(TextEdit textEdit, IStructuredDocumentRegion currentRegion, int indentLevel, int indentStartOffset, String whitespaceRun) {
+		int maxAvailableLineWidth = getFormattingPreferences().getMaxLineWidth();
+
+		int availableLineWidth;
+		String indentString = getIndentString(indentLevel);
+		String lineDelimiter = getLineDelimiter(currentRegion);
+		String newLineAndIndent = lineDelimiter + indentString;
+		
+		TextEdit indentation = null;
+		
+		// if not already correctly indented
+		if (!newLineAndIndent.equals(whitespaceRun)) {
+			if (getFormattingPreferences().getClearAllBlankLines()) {
+				if (whitespaceRun != null) {
+					// replace existing whitespace run
+					indentation = new ReplaceEdit(indentStartOffset, whitespaceRun.length(), newLineAndIndent);
+				}
+				else {
+					// just insert correct indent
+					indentation = new InsertEdit(indentStartOffset, newLineAndIndent);
+				}
+			}
+			// Keep the empty lines
+			else {
+				// just insert correct indent
+				if(whitespaceRun == null)
+					indentation = new InsertEdit(indentStartOffset, newLineAndIndent);
+				// Need to preserve the number of empty lines, but still indent on the current line properly
+				else {
+					String existingDelimiters = extractLineDelimiters(whitespaceRun, currentRegion);
+					if(existingDelimiters != null && existingDelimiters.length() > 0) {
+						String formatted = existingDelimiters + indentString;
+						// Don't perform a replace if the formatted string is the same as the existing whitespaceRun
+						if(!formatted.equals(whitespaceRun))
+							indentation = new ReplaceEdit(indentStartOffset, whitespaceRun.length(), formatted);
+					}
+					// No blank lines to preserve - correct the indent
+					else
+						indentation = new ReplaceEdit(indentStartOffset, whitespaceRun.length(), newLineAndIndent);
+				}
+			}
+		}
+		
+		if(indentation != null)
+			textEdit.addChild(indentation);
+		// update line width
+		availableLineWidth = maxAvailableLineWidth - indentString.length();
+		return availableLineWidth;
+	}
+
+	private int indentIfNotAlreadyIndented(TextEdit textEdit, int indentLevel, IStructuredDocumentRegion currentDocumentRegion, ITextRegion currentTextRegion) {
+		// indent if not already indented
+		int textLength = currentTextRegion.getTextLength();
+		int regionLength = currentTextRegion.getLength();
+		int indentStartOffset = currentDocumentRegion.getTextEndOffset(currentTextRegion);
+		String fullText = currentDocumentRegion.getFullText(currentTextRegion);
+		String whitespaceRun = fullText.substring(textLength, regionLength);
+
+		// update line width
+		int availableLineWidth = indentIfNotAlreadyIndented(textEdit, currentDocumentRegion, indentLevel, indentStartOffset, whitespaceRun);
+		return availableLineWidth;
+	}
+
+	private int indentIfPossible(TextEdit textEdit, XMLFormattingConstraints thisConstraints, IStructuredDocumentRegion currentDocumentRegion, IStructuredDocumentRegion previousDocumentRegion, String whitespaceStrategy, String indentStrategy, boolean addIndent) {
+		return indentIfPossible(textEdit, thisConstraints, currentDocumentRegion, previousDocumentRegion, whitespaceStrategy, indentStrategy, addIndent, !getFormattingPreferences().getClearAllBlankLines());
+	}
+
+	private int indentIfPossible(TextEdit textEdit, XMLFormattingConstraints thisConstraints, IStructuredDocumentRegion currentDocumentRegion, IStructuredDocumentRegion previousDocumentRegion, String whitespaceStrategy, String indentStrategy, boolean addIndent, boolean handlePreviousWhitespace) {
+		int availableLineWidth = -1;
+		// if there is no previous document region, there is no need to indent
+		// because we're at beginning of document
+		if (previousDocumentRegion == null)
+			return availableLineWidth;
+
+		// only indent if ignoring whitespace or if collapsing and
+		// there was a whitespace character before this region
+		boolean canIndent = false;
+		String previousRegionFullText = null;
+		String previousRegionType = null;
+
+		if ((XMLFormattingConstraints.IGNORE.equals(whitespaceStrategy)) || XMLFormattingConstraints.IGNOREANDTRIM.equals(whitespaceStrategy)) {
+			// if ignoring, need to check if previous region was cdata
+			previousRegionType = previousDocumentRegion.getType();
+			if (DOMRegionContext.XML_CDATA_TEXT.equals(previousRegionType))
+				canIndent = false;
+			else
+				canIndent = true;
+		}
+		else if (XMLFormattingConstraints.COLLAPSE.equals(whitespaceStrategy)) {
+			// if collapsing, need to check if previous region ended in a
+			// whitespace
+			previousRegionType = previousDocumentRegion.getType();
+			if (DOMRegionContext.XML_CONTENT.equals(previousRegionType)) {
+				previousRegionFullText = previousDocumentRegion.getFullText();
+				int length = previousRegionFullText.length();
+				if (length > 1)
+					canIndent = Character.isWhitespace(previousRegionFullText.charAt(length - 1));
+			}
+		}
+		if (canIndent) {
+			int indentStartOffset = currentDocumentRegion.getStartOffset();
+			String whitespaceRun = null;
+
+			// get previous region type if it was not previously retrieved
+			if (previousRegionType == null)
+				previousRegionType = previousDocumentRegion.getType();
+
+			// get previous region's text if it was not previously retrieved
+			if (previousRegionFullText == null && DOMRegionContext.XML_CONTENT.equals(previousRegionType))
+				previousRegionFullText = previousDocumentRegion.getFullText();
+
+			// if previous region was only whitespace, this may
+			// already be indented, so need to make sure
+			if ((previousRegionFullText != null) && (previousRegionFullText.trim().length() == 0)) {
+				indentStartOffset = previousDocumentRegion.getStartOffset();
+				whitespaceRun = previousRegionFullText;
+			}
+			if ((previousRegionFullText != null) && (whitespaceRun == null) && handlePreviousWhitespace) {
+				whitespaceRun = getTrailingWhitespace(previousRegionFullText);
+				indentStartOffset = previousDocumentRegion.getEndOffset() - whitespaceRun.length();
+			}
+
+			int indentLevel = thisConstraints.getIndentLevel();
+			if (addIndent && XMLFormattingConstraints.INDENT.equals(indentStrategy)) {
+				++indentLevel;
+				thisConstraints.setIndentLevel(indentLevel);
+			}
+
+			// indent if not already indented
+			availableLineWidth = indentIfNotAlreadyIndented(textEdit, currentDocumentRegion, indentLevel, indentStartOffset, whitespaceRun);
+		}
+		return availableLineWidth;
+	}
+
+	/**
+	 * Allow exactly one whitespace in currentTextRegion. If there are more,
+	 * collapse to one. If there are none, insert one.
+	 * 
+	 * @param textEdit
+	 * @param currentDocumentRegion
+	 * @param availableLineWidth
+	 * @param currentTextRegion
+	 */
+	private void insertSpaceAndCollapse(TextEdit textEdit, IStructuredDocumentRegion currentDocumentRegion, int availableLineWidth, ITextRegion currentTextRegion) {
+		int textLength = currentTextRegion.getTextLength();
+		int regionLength = currentTextRegion.getLength();
+		boolean thereAreSpaces = textLength < regionLength;
+		int spacesStartOffset = currentDocumentRegion.getStartOffset(currentTextRegion) + textLength;
+
+		if (thereAreSpaces) {
+			String fullTagName = currentDocumentRegion.getFullText(currentTextRegion);
+			String whitespaceRun = fullTagName.substring(textLength, regionLength);
+			collapseSpaces(textEdit, spacesStartOffset, availableLineWidth, whitespaceRun);
+		}
+		else {
+			// insert a space
+			InsertEdit insertEdit = new InsertEdit(spacesStartOffset, SPACE);
+			textEdit.addChild(insertEdit);
+		}
+	}
+
+	private boolean shouldIndentBeforeAttribute(XMLFormattingConstraints constraints, ITextRegionList textRegions, int availableLineWidth, int currentTextRegionIndex, ITextRegion currentTextRegion, ITextRegion nextTextRegion) {
+		boolean indentAttribute = false;
+
+		// look ahead to see if going to hit max line width
+		// something attrName
+		int currentWidth = currentTextRegion.getTextLength() + nextTextRegion.getTextLength() + 1;
+		if (currentWidth > availableLineWidth)
+			indentAttribute = true;
+		else {
+			if ((currentTextRegionIndex + 2) < textRegions.size()) {
+				// still okay, so try next region
+				// something attrName=
+				ITextRegion textRegion = textRegions.get(currentTextRegionIndex + 2);
+				if (DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS.equals(textRegion.getType())) {
+					++currentWidth;
+					if (currentWidth > availableLineWidth)
+						indentAttribute = true;
+					else {
+						if ((currentTextRegionIndex + 3) < textRegions.size()) {
+							// still okay, so try next region
+							// something attrName=attrValue
+							textRegion = textRegions.get(currentTextRegionIndex + 3);
+							if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(textRegion.getType())) {
+								currentWidth = +textRegion.getTextLength();
+								if (currentWidth > availableLineWidth)
+									indentAttribute = true;
+							}
+						}
+					}
+				}
+			}
+		}
+		return indentAttribute;
+	}
+
+	/**
+	 * Given the provided information (parentConstraints & currentDOMRegion),
+	 * update the formatting constraints (for this & child)
+	 * 
+	 * @param parentConstraints
+	 *            can be null
+	 * @param thisConstraints
+	 *            can be null
+	 * @param childConstraints
+	 *            can be null
+	 * @param currentDOMRegion
+	 *            cannot be null
+	 */
+	protected void updateFormattingConstraints(XMLFormattingConstraints parentConstraints, XMLFormattingConstraints thisConstraints, XMLFormattingConstraints childConstraints, DOMRegion currentDOMRegion) {
+		IStructuredDocumentRegion currentRegion = currentDOMRegion.documentRegion;
+		IDOMNode currentNode = currentDOMRegion.domNode;
+
+		// default to whatever parent's constraint said to do
+		if (parentConstraints != null) {
+			if (thisConstraints != null) {
+				thisConstraints.copyConstraints(parentConstraints);
+			}
+			if (childConstraints != null) {
+				childConstraints.copyConstraints(parentConstraints);
+				// if whitespace strategy was only a hint, null it out so
+				// defaults are taken instead
+				if (parentConstraints.isWhitespaceStrategyAHint())
+					childConstraints.setWhitespaceStrategy(null);
+			}
+		}
+
+		// set up constraints for direct children of document root
+		Node parentNode = currentNode.getParentNode();
+		if (parentNode != null && parentNode.getNodeType() == Node.DOCUMENT_NODE) {
+			if (thisConstraints != null) {
+				thisConstraints.setWhitespaceStrategy(XMLFormattingConstraints.IGNORE);
+				thisConstraints.setIndentStrategy(XMLFormattingConstraints.NEW_LINE);
+				thisConstraints.setIndentLevel(0);
+			}
+			if (childConstraints != null) {
+				childConstraints.setWhitespaceStrategy(null);
+				childConstraints.setIndentStrategy(null);
+				childConstraints.setIndentLevel(0);
+			}
+		}
+
+		// other conditions to check when setting up child constraints
+		if (childConstraints != null) {
+			XMLFormattingPreferences preferences = getFormattingPreferences();
+
+			// if we're at document root, child tags should always just start
+			// on a new line and have an indent level of 0
+			if (currentNode.getNodeType() == Node.DOCUMENT_NODE) {
+				childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.IGNORE);
+				childConstraints.setIndentStrategy(XMLFormattingConstraints.NEW_LINE);
+				childConstraints.setIndentLevel(0);
+			}
+			else {
+				// BUG108074 & BUG84688 - preserve whitespace in xsl:text &
+				// xsl:attribute
+				String nodeNamespaceURI = currentNode.getNamespaceURI();
+				if (XSL_NAMESPACE.equals(nodeNamespaceURI)) {
+					String nodeName = ((Element) currentNode).getLocalName();
+					if (XSL_ATTRIBUTE.equals(nodeName) || XSL_TEXT.equals(nodeName)) {
+						childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.PRESERVE);
+					}
+				}
+				else {
+					// search within current tag for xml:space attribute
+					ITextRegionList textRegions = currentRegion.getRegions();
+					int i = 0;
+					boolean xmlSpaceFound = false;
+					boolean preserveFound = false;
+					while (i < textRegions.size() && !xmlSpaceFound) {
+						ITextRegion textRegion = textRegions.get(i);
+						if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(textRegion.getType())) {
+							String regionText = currentRegion.getText(textRegion);
+							if (XML_SPACE.equals(regionText)) {
+								if ((i + 1) < textRegions.size()) {
+									++i;
+									textRegion = textRegions.get(i);
+									if (DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS.equals(textRegion.getType()) && ((i + 1) < textRegions.size())) {
+										++i;
+										textRegion = textRegions.get(i);
+										regionText = currentRegion.getText(textRegion);
+										if (PRESERVE.equals(regionText) || PRESERVE_QUOTED.equals(regionText)) {
+											preserveFound = true;
+										}
+									}
+								}
+								xmlSpaceFound = true;
+							}
+						}
+						++i;
+					}
+					if (xmlSpaceFound) {
+						if (preserveFound) {
+							// preserve was found so set the strategy
+							childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.PRESERVE);
+						}
+						else {
+							// xml:space was found but it was not collapse, so
+							// use default whitespace strategy
+							childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.DEFAULT);
+						}
+					}
+					else {
+						// how to hande nodes that have nonwhitespace text
+						// content
+						NodeList nodeList = currentNode.getChildNodes();
+						int length = nodeList.getLength();
+						int index = 0;
+						boolean textNodeFound = false;
+						// BUG214516 - If the parent constraint is to preserve whitespace, child constraints should
+						// still reflect the parent constraints
+						while (index < length && !textNodeFound && parentConstraints != null && !XMLFormattingConstraints.PRESERVE.equals(parentConstraints.getWhitespaceStrategy())) {
+							Node childNode = nodeList.item(index);
+							if (childNode.getNodeType() == Node.TEXT_NODE) {
+								textNodeFound = !((IDOMText) childNode).isElementContentWhitespace();
+							}
+							++index;
+						}
+						if (textNodeFound) {
+							if (length > 1) {
+								// more in here than just text, so consider
+								// this mixed content
+								childConstraints.setWhitespaceStrategy(preferences.getMixedWhitespaceStrategy());
+								childConstraints.setIndentStrategy(preferences.getMixedIndentStrategy());
+							}
+							else {
+								// there's only text
+								childConstraints.setWhitespaceStrategy(preferences.getTextWhitespaceStrategy());
+								childConstraints.setIndentStrategy(preferences.getTextIndentStrategy());
+							}
+							childConstraints.setIsWhitespaceStrategyAHint(true);
+							childConstraints.setIsIndentStrategyAHint(true);
+						}
+
+						// try referring to content model for information on
+						// whitespace & indent strategy
+						ModelQueryAdapter adapter = (ModelQueryAdapter) ((IDOMDocument) currentNode.getOwnerDocument()).getAdapterFor(ModelQueryAdapter.class);
+						CMElementDeclaration elementDeclaration = (CMElementDeclaration) adapter.getModelQuery().getCMNode(currentNode);
+						if (elementDeclaration != null) {
+							// follow whitespace strategy preference for
+							// pcdata content
+							int contentType = elementDeclaration.getContentType();
+							
+							String facetValue = null;
+							if(elementDeclaration.getDataType() != null)
+								facetValue = (String) elementDeclaration.getDataType().getProperty(PROPERTY_WHITESPACE_FACET);
+							if(facetValue != null) {
+								if(PRESERVE.equals(facetValue))
+									childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.PRESERVE);
+								// For XSD types, "collapse" corresponds to the IGNOREANDTRIM strategy
+								else if(COLLAPSE.equals(facetValue))
+									childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.IGNOREANDTRIM);
+								else if(REPLACE.equals(facetValue))
+									childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.REPLACE);
+							}
+							else if (contentType == CMElementDeclaration.PCDATA && parentConstraints != null && !XMLFormattingConstraints.PRESERVE.equals(parentConstraints.getWhitespaceStrategy())) {
+								childConstraints.setWhitespaceStrategy(preferences.getPCDataWhitespaceStrategy());
+							}
+							else if (contentType == CMElementDeclaration.ELEMENT && parentConstraints != null && !XMLFormattingConstraints.PRESERVE.equals(parentConstraints.getWhitespaceStrategy())) {
+								childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.IGNORE);
+								childConstraints.setIndentStrategy(XMLFormattingConstraints.INDENT);
+								childConstraints.setIsWhitespaceStrategyAHint(true);
+								childConstraints.setIsIndentStrategyAHint(true);
+							}
+							else {
+								// look for xml:space in content model
+								CMNamedNodeMap cmAttributes = elementDeclaration.getAttributes();
+
+								// Not needed - we're looking for xml:space
+								//CMNamedNodeMapImpl allAttributes = new CMNamedNodeMapImpl(cmAttributes);
+								//List nodes = ModelQueryUtil.getModelQuery(currentNode.getOwnerDocument()).getAvailableContent((Element) currentNode, elementDeclaration, ModelQuery.INCLUDE_ATTRIBUTES);
+								//for (int k = 0; k < nodes.size(); k++) {
+								//	CMNode cmnode = (CMNode) nodes.get(k);
+								//	if (cmnode.getNodeType() == CMNode.ATTRIBUTE_DECLARATION) {
+								//		allAttributes.put(cmnode);
+								//	}
+								//}
+								//cmAttributes = allAttributes;
+
+								// Check implied values from the DTD way.
+								CMAttributeDeclaration attributeDeclaration = (CMAttributeDeclaration) cmAttributes.getNamedItem(XML_SPACE);
+								if (attributeDeclaration != null) {
+									// CMAttributeDeclaration found, check
+									// it
+									// out.
+
+									//BUG214516/196544 - Fixed NPE that was caused by an attr having
+									// a null attr type
+									String defaultValue = null;
+									CMDataType attrType = attributeDeclaration.getAttrType();
+									if (attrType != null) {
+										if ((attrType.getImpliedValueKind() != CMDataType.IMPLIED_VALUE_NONE) && attrType.getImpliedValue() != null)
+											defaultValue = attrType.getImpliedValue();
+										else if ((attrType.getEnumeratedValues() != null) && (attrType.getEnumeratedValues().length > 0)) {
+											defaultValue = attrType.getEnumeratedValues()[0];
+										}
+									}
+
+									// xml:space="preserve" means preserve
+									// space,
+									// everything else means back to
+									// default.
+									if (PRESERVE.equals(defaultValue))
+										childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.PRESERVE);
+									else
+										childConstraints.setWhitespaceStrategy(XMLFormattingConstraints.DEFAULT);
+								}
+								// If the node has no attributes, inherit the parents whitespace strategy
+								else {
+									if (parentConstraints != null)
+										childConstraints.setWhitespaceStrategy(parentConstraints.getWhitespaceStrategy());
+									else
+										childConstraints.setWhitespaceStrategy(null);
+								}
+							}
+						}
+					}
+				}
+			}
+			// set default values according to preferences
+			if (childConstraints.getWhitespaceStrategy() == null) {
+				childConstraints.setWhitespaceStrategy(preferences.getElementWhitespaceStrategy());
+			}
+			if (childConstraints.getIndentStrategy() == null) {
+				childConstraints.setIndentStrategy(preferences.getElementIndentStrategy());
+			}
+		}
+	}
+
+	/**
+	 * Calculates the current available line width given fullText.
+	 * 
+	 * @param fullText
+	 * @param availableLineWidth
+	 * @param maxAvailableLineWidth
+	 * @return
+	 */
+	private int updateLineWidthWithLastLine(String fullText, int availableLineWidth) {
+		int maxAvailableLineWidth = getFormattingPreferences().getMaxLineWidth();
+		int lineWidth = availableLineWidth;
+		if (fullText != null) {
+			int textLength = fullText.length();
+			// update available line width
+			// find last newline
+			int lastLFOffset = fullText.lastIndexOf('\n');
+			int lastCROffset = fullText.lastIndexOf('\r');
+			// all text was on 1 line
+			if (lastLFOffset == -1 && lastCROffset == -1) {
+				// just subtract text length from current
+				// available line width
+				lineWidth -= fullText.length();
+			}
+			else {
+				// calculate available line width of last line
+				int lastNewLine = Math.max(lastLFOffset, lastCROffset);
+				lineWidth = maxAvailableLineWidth - (textLength - lastNewLine);
+			}
+		}
+		return lineWidth;
+	}
+
+	private String getLineDelimiter(IStructuredDocumentRegion currentRegion) {
+		IStructuredDocument doc = currentRegion.getParentDocument();
+		int line = doc.getLineOfOffset(currentRegion.getStartOffset());
+		String lineDelimiter = doc.getLineDelimiter();
+		try {
+			if (line > 0) {
+				lineDelimiter = doc.getLineDelimiter(line - 1);
+			}
+		}
+		catch (BadLocationException e) {
+			// log for now, unless we find reason not to
+			Logger.log(Logger.INFO, e.getMessage());
+		}
+		// BUG115716: if cannot get line delimiter from current line, just
+		// use default line delimiter
+		if (lineDelimiter == null)
+			lineDelimiter = doc.getLineDelimiter();
+		return lineDelimiter;
+	}
+	
+	private String extractLineDelimiters(String base, IStructuredDocumentRegion currentRegion) {
+		String lineDelimiter = getLineDelimiter(currentRegion);
+		StringBuffer sb = new StringBuffer();
+		for(int index = 0; index < base.length();) {
+			index = base.indexOf(lineDelimiter, index);
+			if(index++ >= 0)
+				sb.append(lineDelimiter);
+			else
+				break;
+		}
+		return sb.toString();
+	}
+
+	void setProgressMonitor(IProgressMonitor monitor) {
+		fProgressMonitor = monitor;
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.wst.xml.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.xml.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..844964d
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,66 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.wst.xml.ui; singleton:=true
+Bundle-Version: 1.1.201.qualifier
+Bundle-Activator: org.eclipse.wst.xml.ui.internal.XMLUIPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.wst.xml.ui,
+ org.eclipse.wst.xml.ui.internal;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.actions;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.autoedit;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.catalog;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.contentassist;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.contentoutline;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.correction;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.dialogs;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.dnd;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.doubleclick;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.editor;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.editor.propertytester,
+ org.eclipse.wst.xml.ui.internal.handlers;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.hyperlink;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.nsedit;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.perspective;x-friends:="org.eclipse.wst.xsl.ui",
+ org.eclipse.wst.xml.ui.internal.preferences;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.projection;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.properties;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.provisional;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.quickoutline,
+ org.eclipse.wst.xml.ui.internal.registry;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.search;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.selection;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.spelling,
+ org.eclipse.wst.xml.ui.internal.style;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.tabletree;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.taginfo;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.templates;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.text;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.util;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.validation;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.validation.core.errorinfo;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.internal.views.annotations,
+ org.eclipse.wst.xml.ui.internal.views.contentmodel,
+ org.eclipse.wst.xml.ui.internal.wizards;x-friends:="org.eclipse.wst.xsd.ui,org.eclipse.wst.dtd.ui",
+ org.eclipse.wst.xml.ui.views.contentoutline,
+ org.eclipse.wst.xml.ui.views.properties
+Import-Package: com.ibm.icu.util; version="3.8",
+ com.ibm.icu.text; version="3.8"
+Require-Bundle: org.eclipse.ui.ide;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.views;bundle-version="[3.4.0,4.0.0)",
+ org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ui.workbench.texteditor;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.sse.ui;bundle-version="[1.2.0,1.4.0)",
+ org.eclipse.wst.sse.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.ui;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.wst.common.uriresolver;bundle-version="[1.1.301,1.2.0)",
+ org.eclipse.wst.xml.core;bundle-version="[1.1.500,1.2.0)",
+ org.eclipse.wst.common.ui;bundle-version="[1.1.400,1.2.0)",
+ org.eclipse.wst.validation;bundle-version="[1.2.100,1.3.0)",
+ org.eclipse.core.expressions;bundle-version="[3.4.100,4.0.0)",
+ org.eclipse.core.filesystem;bundle-version="[1.3.0,2.0.0)"
+Bundle-ActivationPolicy: lazy; exclude:="org.eclipse.wst.xml.ui.internal.validation.core.errorinfo,org.eclipse.wst.xml.ui.internal.editor.propertytester"
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/bundles/org.eclipse.wst.xml.ui/src-multipage/org/eclipse/wst/xml/ui/internal/tabletree/XMLMultiPageEditorPart.java b/bundles/org.eclipse.wst.xml.ui/src-multipage/org/eclipse/wst/xml/ui/internal/tabletree/XMLMultiPageEditorPart.java
new file mode 100644
index 0000000..59f8da3
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.ui/src-multipage/org/eclipse/wst/xml/ui/internal/tabletree/XMLMultiPageEditorPart.java
@@ -0,0 +1,1164 @@
+/*****************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others. All rights reserved. This
+ * program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and
+ * is available at http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ****************************************************************************/
+package org.eclipse.wst.xml.ui.internal.tabletree;
+
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.util.ConfigureColumns;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.window.SameShellProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.IEditorActionBarContributor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.INavigationLocation;
+import org.eclipse.ui.INavigationLocationProvider;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.ide.IGotoMarker;
+import org.eclipse.ui.part.MultiPageEditorActionBarContributor;
+import org.eclipse.ui.part.MultiPageEditorPart;
+import org.eclipse.ui.part.MultiPageEditorSite;
+import org.eclipse.ui.part.MultiPageSelectionProvider;
+import org.eclipse.ui.progress.UIJob;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.TextSelectionNavigationLocation;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.ui.StructuredTextEditor;
+import org.eclipse.wst.sse.ui.internal.contentoutline.IJFaceNodeAdapter;
+import org.eclipse.wst.xml.core.internal.provisional.IXMLPreferenceNames;
+import org.eclipse.wst.xml.core.internal.provisional.contenttype.ContentTypeIdForXML;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.eclipse.wst.xml.ui.internal.Logger;
+import org.eclipse.wst.xml.ui.internal.XMLUIPlugin;
+import org.eclipse.wst.xml.ui.internal.contentoutline.JFaceNodeLabelProvider;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class XMLMultiPageEditorPart extends MultiPageEditorPart implements INavigationLocationProvider {
+
+	/**
+	 * Internal part activation listener, copied from AbstractTextEditor
+	 */
+	class ActivationListener implements IPartListener, IWindowListener {
+
+		/**
+		 * The maximum number of children the root nodes can have for the
+		 * design page to auto expand the root nodes
+		 */
+		private static final int MAX_NUM_CHILD_NODES_FOR_AUTO_EXPAND = 500;
+		
+		/** Cache of the active workbench part. */
+		private IWorkbenchPart fActivePart;
+		/** Indicates whether activation handling is currently be done. */
+		private boolean fIsHandlingActivation = false;
+		/**
+		 * The part service.
+		 * 
+		 * @since 3.1
+		 */
+		private IPartService fPartService;
+
+		/**
+		 * Creates this activation listener.
+		 * 
+		 * @param partService
+		 *            the part service on which to add the part listener
+		 * @since 3.1
+		 */
+		public ActivationListener(IPartService partService) {
+			fPartService = partService;
+			fPartService.addPartListener(this);
+			PlatformUI.getWorkbench().addWindowListener(this);
+		}
+
+		/**
+		 * Disposes this activation listener.
+		 * 
+		 * @since 3.1
+		 */
+		public void dispose() {
+			fPartService.removePartListener(this);
+			PlatformUI.getWorkbench().removeWindowListener(this);
+			fPartService = null;
+		}
+
+		/*
+		 * @see IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart)
+		 */
+		public void partActivated(IWorkbenchPart part) {
+			fActivePart = part;
+			handleActivation();
+		}
+
+		/*
+		 * @see IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart)
+		 */
+		public void partBroughtToTop(IWorkbenchPart part) {
+			// do nothing
+		}
+
+		/*
+		 * @see IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart)
+		 */
+		public void partClosed(IWorkbenchPart part) {
+			// do nothing
+		}
+
+		/*
+		 * @see IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart)
+		 */
+		public void partDeactivated(IWorkbenchPart part) {
+			fActivePart = null;
+		}
+
+		/*
+		 * @see IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
+		 */
+		public void partOpened(IWorkbenchPart part) {
+			if (fDesignViewer instanceof AbstractTreeViewer) {
+				IDocument document = getDocument();
+				if (document != null) {
+					IStructuredModel model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+					try {
+						if (model instanceof IDOMModel) {
+							IDOMDocument modelDocument = ((IDOMModel) model).getDocument();
+							NodeList rootChildren = modelDocument.getChildNodes();
+
+							boolean tooManyChildren = (rootChildren.getLength() > MAX_NUM_CHILD_NODES_FOR_AUTO_EXPAND);
+							/*
+							 * For each root (there should really only be one
+							 * real root but there are also could be empty
+							 * text regions and doc type at the root level)
+							 * determine if it has to many children or not to
+							 * auto expand
+							 */
+							for (int i = 0; i < rootChildren.getLength() && !tooManyChildren; ++i) {
+								tooManyChildren = (rootChildren.item(i).getChildNodes().getLength() > MAX_NUM_CHILD_NODES_FOR_AUTO_EXPAND);
+							}
+
+							/*
+							 * if root node does not have to many children
+							 * then auto expand the root node
+							 */
+							if (!tooManyChildren) {
+								((AbstractTreeViewer) fDesignViewer).expandToLevel(2);
+							}
+						}
+					}
+					finally {
+						if (model != null) {
+							model.releaseFromRead();
+						}
+					}
+				}
+			}
+		}
+
+		/**
+		 * Handles the activation triggering a element state check in the
+		 * editor.
+		 */
+		void handleActivation() {
+			if (fIsHandlingActivation || (getTextEditor() == null)) {
+				return;
+			}
+
+			if (fActivePart == XMLMultiPageEditorPart.this) {
+				fIsHandlingActivation = true;
+				try {
+					getTextEditor().safelySanityCheckState(getEditorInput());
+				}
+				finally {
+					fIsHandlingActivation = false;
+				}
+			}
+		}
+
+		/*
+		 * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow)
+		 * @since 3.1
+		 */
+		public void windowActivated(IWorkbenchWindow window) {
+			if (window == getEditorSite().getWorkbenchWindow()) {
+				/*
+				 * Workaround for problem described in
+				 * http://dev.eclipse.org/bugs/show_bug.cgi?id=11731 Will be
+				 * removed when SWT has solved the problem.
+				 */
+				window.getShell().getDisplay().asyncExec(new Runnable() {
+					public void run() {
+						handleActivation();
+					}
+				});
+			}
+		}
+
+		/*
+		 * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow)
+		 * @since 3.1
+		 */
+		public void windowDeactivated(IWorkbenchWindow window) {
+			// do nothing
+		}
+
+		/*
+		 * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow)
+		 * @since 3.1
+		 */
+		public void windowClosed(IWorkbenchWindow window) {
+			// do nothing
+		}
+
+		/*
+		 * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow)
+		 * @since 3.1
+		 */
+		public void windowOpened(IWorkbenchWindow window) {
+			// do nothing
+		}
+	}
+
+	/**
+	 * Listens for selection from the source page, applying it to the design
+	 * viewer.
+	 */
+	private class TextEditorPostSelectionAdapter extends UIJob implements ISelectionChangedListener {
+		boolean forcePostSelection = false;
+		ISelection selection = null;
+
+		public TextEditorPostSelectionAdapter() {
+			super(getTitle());
+			setUser(true);
+		}
+
+		public IStatus runInUIThread(IProgressMonitor monitor) {
+			if (selection != null) {
+				fDesignViewer.getSelectionProvider().setSelection(selection);
+			}
+			return Status.OK_STATUS;
+		}
+
+		public void selectionChanged(SelectionChangedEvent event) {
+			if ((fDesignViewer != null) && ((getActivePage() != fDesignPageIndex) || !XMLMultiPageEditorPart.this.equals(getSite().getPage().getActivePart()))) {
+				if (forcePostSelection) {
+					selection = event.getSelection();
+					schedule(200);
+				}
+				else {
+					fDesignViewer.getSelectionProvider().setSelection(event.getSelection());
+				}
+			}
+		}
+	}
+
+	private class PageInitializationData {
+		IConfigurationElement fElement;
+		String fPropertyName;
+		Object fData;
+
+		PageInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
+			super();
+			fElement = cfig;
+			fPropertyName = propertyName;
+			fData = data;
+		}
+
+		void sendInitializationData(IExecutableExtension executableExtension) {
+			try {
+				executableExtension.setInitializationData(fElement, fPropertyName, fData);
+			}
+			catch (CoreException e) {
+				Logger.logException(e);
+			}
+		}
+	}
+
+	/**
+	 * Internal IPropertyListener on the source page
+	 */
+	class PropertyListener implements IPropertyListener {
+		public void propertyChanged(Object source, int propId) {
+			switch (propId) {
+				// had to implement input changed "listener" so that
+				// StructuredTextEditor could tell it containing editor that
+				// the input has change, when a 'resource moved' event is
+				// found.
+				case IEditorPart.PROP_INPUT : {
+					if (source == getTextEditor() && fDesignViewer instanceof XMLTableTreeViewer) {
+						IStructuredModel model = StructuredModelManager.getModelManager().getExistingModelForRead(getDocument());
+						try {
+							if (model instanceof IDOMModel) {
+								IDOMDocument modelDocument = ((IDOMModel) model).getDocument();
+								Object designInput = ((XMLTableTreeViewer) fDesignViewer).getInput();
+								if (modelDocument != designInput)
+									setInput(getTextEditor().getEditorInput());
+							}
+						}
+						finally {
+							if (model != null)
+								model.releaseFromRead();
+						}
+					}
+				}
+				case IEditorPart.PROP_DIRTY : {
+					if (source == getTextEditor()) {
+						if (getTextEditor().getEditorInput() != getEditorInput()) {
+							setInput(getTextEditor().getEditorInput());
+							/*
+							 * title should always change when input changes.
+							 * create runnable for following post call
+							 */
+							Runnable runnable = new Runnable() {
+								public void run() {
+									_firePropertyChange(IWorkbenchPart.PROP_TITLE);
+								}
+							};
+							/*
+							 * Update is just to post things on the display
+							 * queue (thread). We have to do this to get the
+							 * dirty property to get updated after other
+							 * things on the queue are executed.
+							 */
+							((Control) getTextEditor().getAdapter(Control.class)).getDisplay().asyncExec(runnable);
+						}
+					}
+					break;
+				}
+				case IWorkbenchPart.PROP_TITLE : {
+					// update the input if the title is changed
+					if (source == getTextEditor()) {
+						if (getTextEditor().getEditorInput() != getEditorInput()) {
+							setInput(getTextEditor().getEditorInput());
+						}
+					}
+					break;
+				}
+				default : {
+					// propagate changes. Is this needed? Answer: Yes.
+					if (source == getTextEditor()) {
+						_firePropertyChange(propId);
+					}
+					break;
+				}
+			}
+
+		}
+	}
+
+	class TextInputListener implements ITextInputListener {
+		public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+			// do nothing
+		}
+
+		public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+			if ((fDesignViewer != null) && (newInput != null)) {
+				fDesignViewer.setDocument(newInput);
+			}
+		}
+	}
+
+	class StatusLineLabelProvider extends JFaceNodeLabelProvider {
+		public StatusLineLabelProvider() {
+		}
+
+		public String getText(Object element) {
+			if (element == null)
+				return null;
+
+			Node node = (Node) element;
+			if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
+				return getText(((Attr) node).getOwnerElement());
+			}
+
+			StringBuffer s = new StringBuffer();
+			if (node.getNodeType() != Node.DOCUMENT_NODE) {
+				while (node != null && node instanceof INodeNotifier) {
+					INodeNotifier notifier = (INodeNotifier) node;
+					if (node.getNodeType() != Node.DOCUMENT_NODE) {
+						IJFaceNodeAdapter adapter = (IJFaceNodeAdapter) notifier.getAdapterFor(IJFaceNodeAdapter.class);
+						if (adapter != null) {
+							s.insert(0, adapter.getLabelText(node));
+						}
+					}
+					node = node.getParentNode();
+					if (node != null && node.getNodeType() != Node.DOCUMENT_NODE)
+						s.insert(0, IPath.SEPARATOR);
+				}
+			}
+			return s.toString();
+		}
+
+		public Image getImage(Object element) {
+			if (element == null)
+				return null;
+
+			Node node = (Node) element;
+			if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
+				return getImage(((Attr) node).getOwnerElement());
+			}
+			return super.getImage(element);
+		}
+	}
+
+	/** The design page index. */
+	private int fDesignPageIndex;
+
+	/** The design viewer */
+	IDesignViewer fDesignViewer;
+	/** Any container for the design viewer */
+	private Composite fDesignContainer;
+
+	private ActivationListener fActivationListener;
+
+	IPropertyListener fPropertyListener = null;
+
+	/** The source page index. */
+	int fSourcePageIndex;
+
+	/** The text editor. */
+	private StructuredTextEditor fTextEditor;
+
+	private TextEditorPostSelectionAdapter fTextEditorSelectionListener;
+
+	private ILabelProvider fStatusLineLabelProvider;
+
+	private PageInitializationData fPageInitializer;
+	
+	private ToolBarManager fToolbarManager;
+	private ToolBarManager fEditorManager;
+
+	/** Context menu manager */
+	private MenuManager fMenuManager;
+
+	private boolean fAllocateToolbar = true;
+	
+	/**
+	 * StructuredTextMultiPageEditorPart constructor comment.
+	 */
+	public XMLMultiPageEditorPart() {
+		super();
+		fStatusLineLabelProvider = new StatusLineLabelProvider();
+	}
+
+	/*
+	 * This method is just to make firePropertyChanged accessible from some
+	 * (anonomous) inner classes.
+	 */
+	void _firePropertyChange(int property) {
+		super.firePropertyChange(property);
+	}
+
+	/**
+	 * Adds the source page of the multi-page editor.
+	 */
+	private void addSourcePage() throws PartInitException {
+		fSourcePageIndex = addPage(fTextEditor, getEditorInput());
+		setPageText(fSourcePageIndex, XMLEditorMessages.XMLMultiPageEditorPart_0);
+
+		firePropertyChange(PROP_TITLE);
+
+		// Changes to the Text Viewer's document instance should also
+		// force an
+		// input refresh
+		fTextEditor.getTextViewer().addTextInputListener(new TextInputListener());
+	}
+
+	/**
+	 * Connects the design viewer with the viewer selection manager. Should be
+	 * done after createSourcePage() is done because we need to get the
+	 * ViewerSelectionManager from the TextEditor. setModel is also done here
+	 * because getModel() needs to reference the TextEditor.
+	 */
+	private void connectDesignPage() {
+		if (fDesignViewer != null) {
+			fDesignViewer.setDocument(getDocument());
+		}
+
+		/*
+		 * Connect selection from the Design page to the selection provider
+		 * for the XMLMultiPageEditorPart so that selection changes in the
+		 * Design page will propagate across the workbench
+		 */
+		if (fDesignViewer.getSelectionProvider() instanceof IPostSelectionProvider) {
+			((IPostSelectionProvider) fDesignViewer.getSelectionProvider()).addPostSelectionChangedListener(new ISelectionChangedListener() {
+				public void selectionChanged(SelectionChangedEvent event) {
+					if (getActivePage() != fSourcePageIndex) {
+						((MultiPageSelectionProvider) getSite().getSelectionProvider()).firePostSelectionChanged(event);
+					}
+				}
+			});
+		}
+		fDesignViewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				if (getActivePage() != fSourcePageIndex) {
+					((MultiPageSelectionProvider) getSite().getSelectionProvider()).fireSelectionChanged(event);
+				}
+			}
+		});
+
+		fDesignViewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				if (getActivePage() != fSourcePageIndex) {
+					try {
+						updateStatusLine(event.getSelection());
+					}
+					catch (Exception exception) {
+						Logger.logException(exception);
+					}						
+				}
+			}
+		});
+
+		/*
+		 * Handle double-click in the Design page by selecting the
+		 * corresponding amount of text in the Source page.
+		 * 
+		 * Warning: This implies more knowledge of the design viewer's underlying
+		 * Control than expressed in the IDesignViewer interface
+		 */
+		fDesignViewer.getControl().addListener(SWT.MouseDoubleClick, new Listener() {
+			public void handleEvent(Event event) {
+				ISelection selection = fDesignViewer.getSelectionProvider().getSelection();
+				int start = -1;
+				int length = -1;
+				if (selection instanceof IStructuredSelection) {
+					/*
+					 * selection goes from the start of the first object to
+					 * the end of the last
+					 */
+					IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+					Object o = structuredSelection.getFirstElement();
+					Object o2 = null;
+					if (structuredSelection.size() > 1) {
+						o2 = structuredSelection.toArray()[structuredSelection.size() - 1];
+					}
+					else {
+						o2 = o;
+					}
+					if (o instanceof IndexedRegion) {
+						start = ((IndexedRegion) o).getStartOffset();
+						length = ((IndexedRegion) o2).getEndOffset() - start;
+					}
+					else if (o2 instanceof ITextRegion) {
+						start = ((ITextRegion) o).getStart();
+						length = ((ITextRegion) o2).getEnd() - start;
+					}
+				}
+				else if (selection instanceof ITextSelection) {
+					start = ((ITextSelection) selection).getOffset();
+					length = ((ITextSelection) selection).getLength();
+				}
+				if ((start > -1) && (length > -1)) {
+					getTextEditor().selectAndReveal(start, length);
+				}
+			}
+		});
+
+		/*
+		 * Connect selection from the Source page to the selection provider of
+		 * the Design page so that selection in the Source page will drive
+		 * selection in the Design page. Prefer post selection.
+		 */
+		ISelectionProvider provider = getTextEditor().getSelectionProvider();
+		if (fTextEditorSelectionListener == null) {
+			fTextEditorSelectionListener = new TextEditorPostSelectionAdapter();
+		}
+		if (provider instanceof IPostSelectionProvider) {
+			fTextEditorSelectionListener.forcePostSelection = false;
+			((IPostSelectionProvider) provider).addPostSelectionChangedListener(fTextEditorSelectionListener);
+		}
+		else {
+			fTextEditorSelectionListener.forcePostSelection = true;
+			provider.addSelectionChangedListener(fTextEditorSelectionListener);
+		}
+	}
+
+	/**
+	 * Create and Add the Design Page using a registered factory
+	 * 
+	 */
+	private void createAndAddDesignPage() {
+		IDesignViewer designViewer = createDesignPage();
+
+		fDesignViewer = designViewer;
+		// note: By adding the design page as a Control instead of an
+		// IEditorPart, page switches will indicate
+		// a "null" active editor when the design page is made active
+		if (fDesignContainer != null)
+			fDesignPageIndex = addPage(fDesignContainer);
+		else
+			fDesignPageIndex = addPage(designViewer.getControl());
+			
+		setPageText(fDesignPageIndex, designViewer.getTitle());
+	}
+
+	protected IDesignViewer createDesignPage() {		
+		Composite container = getDesignContainer(getContainer());
+
+		XMLTableTreeViewer tableTreeViewer = new XMLTableTreeViewer(container);
+		tableTreeViewer.getControl().setLayoutData(new GridData(GridData.FILL_BOTH));
+		// Set the default info-pop for XML design viewer.
+		XMLUIPlugin.getInstance().getWorkbench().getHelpSystem().setHelp(tableTreeViewer.getControl(), XMLTableTreeHelpContextIds.XML_DESIGN_VIEW_HELPID);
+
+		// Toolbar wasn't allocated
+		if (fToolbarManager != null && fEditorManager != null) {
+			addToolBarActions(tableTreeViewer);
+			addEditorActions(tableTreeViewer);
+		}
+		return tableTreeViewer;
+	}
+
+	protected Composite getDesignContainer(Composite defaultContainer) {
+		Composite container = defaultContainer;
+		// create a container to hold the toolbar if it should be created
+		if (fAllocateToolbar) {
+			container = new Composite(defaultContainer, SWT.NONE);
+			GridLayout layout = new GridLayout();
+			layout.marginHeight = 0;
+			layout.verticalSpacing = 0;
+			layout.marginWidth = 0;
+			container.setLayout(layout);
+
+			Composite toolbarContainer = new Composite(container, SWT.NONE);
+			layout = new GridLayout();
+			layout.marginHeight = 0;
+			layout.verticalSpacing = 0;
+			layout.marginWidth = 0;
+			layout.horizontalSpacing = 0;
+			layout.numColumns = 2;
+			toolbarContainer.setLayout(layout);
+			toolbarContainer.setLayoutData(new GridData(GridData.END, GridData.VERTICAL_ALIGN_BEGINNING, true, false));
+
+			ToolBar tb = new ToolBar(toolbarContainer, SWT.FLAT | SWT.NO_BACKGROUND);
+			fToolbarManager = new ToolBarManager(tb);
+			tb.setLayoutData(new GridData(GridData.END, GridData.VERTICAL_ALIGN_BEGINNING, true, false));
+			tb = new ToolBar(toolbarContainer, SWT.FLAT | SWT.NO_BACKGROUND);
+			fEditorManager = new ToolBarManager(tb);
+			tb.setLayoutData(new GridData(GridData.END, GridData.VERTICAL_ALIGN_BEGINNING, true, false));
+			fDesignContainer = container;
+		}
+		return container;
+	}
+
+	private class EditorActions extends Action {
+		private ToolBar fToolbar;
+
+		public EditorActions(ToolBar toolbar) {
+			fToolbar = toolbar;
+		}
+
+		public ImageDescriptor getImageDescriptor() {
+			return XMLEditorPluginImageHelper.getInstance().getImageDescriptor(XMLEditorPluginImageHelper.EDITOR_MENU);
+		}
+
+		public String getToolTipText() {
+			return XMLEditorMessages.EditorMenu_tooltip;
+		}
+
+		public void run() {
+			Menu menu = fMenuManager.createContextMenu(fDesignContainer);
+			Point size = fToolbar.getSize();
+			Point location = fToolbar.toDisplay(0, size.y);
+			menu.setLocation(location.x, location.y);
+			menu.setVisible(true);
+		}
+	}
+
+	private void addToolBarActions(IDesignViewer viewer) {
+		if (viewer instanceof AbstractTreeViewer) {
+			ViewerExpandCollapseAction expand = new ViewerExpandCollapseAction(true);
+			ViewerExpandCollapseAction collapse = new ViewerExpandCollapseAction(false);
+			fToolbarManager.add(expand);
+			fToolbarManager.add(collapse);
+			fToolbarManager.update(true);
+	
+	
+			expand.setViewer((AbstractTreeViewer) viewer);
+			collapse.setViewer((AbstractTreeViewer) viewer);
+		}
+	}
+
+	private void addEditorActions(IDesignViewer viewer) {
+		if (viewer instanceof AbstractTreeViewer) {
+			final Tree tree = (Tree) ((AbstractTreeViewer) viewer).getControl();
+			fMenuManager = new MenuManager();
+			fMenuManager.add(new Action(XMLEditorMessages.ConfigureColumns_label) {
+				public void run() {
+					ConfigureColumns.forTree(tree , new SameShellProvider(tree));
+				}
+			});
+			getSite().registerContextMenu("org.eclipse.wst.xml.ui.editor", fMenuManager, getSite().getSelectionProvider()); //$NON-NLS-1$
+			fMenuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+
+			fEditorManager.add(new EditorActions(fEditorManager.getControl()));
+			fEditorManager.update(true);
+		}
+	}
+
+	/**
+	 * Creates the pages of this multi-page editor.
+	 * <p>
+	 * Subclasses of <code>MultiPageEditor</code> must implement this method.
+	 * </p>
+	 */
+	protected void createPages() {
+		try {
+			// source page MUST be created before design page, now
+			createSourcePage();
+
+			createAndAddDesignPage();
+			addSourcePage();
+			connectDesignPage();
+
+			// set the active editor in the action bar contributor first
+			// before setactivepage calls action bar contributor's
+			// setactivepage (bug141013 - remove when bug151488 is fixed)
+			IEditorActionBarContributor contributor = getEditorSite().getActionBarContributor();
+			if (contributor instanceof MultiPageEditorActionBarContributor) {
+				((MultiPageEditorActionBarContributor) contributor).setActiveEditor(this);
+			}
+
+			int activePageIndex = getPreferenceStore().getInt(getEditorSite().getId() + "." + IXMLPreferenceNames.LAST_ACTIVE_PAGE); //$NON-NLS-1$;
+			if ((activePageIndex >= 0) && (activePageIndex < getPageCount())) {
+				setActivePage(activePageIndex);
+			}
+			else {
+				setActivePage(fSourcePageIndex);
+			}
+		}
+		catch (PartInitException e) {
+			Logger.logException(e);
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * @see org.eclipse.ui.part.MultiPageEditorPart#createSite(org.eclipse.ui.IEditorPart)
+	 */
+	protected IEditorSite createSite(IEditorPart editor) {
+		IEditorSite site = null;
+		if (editor == fTextEditor) {
+			site = new MultiPageEditorSite(this, editor) {
+				/**
+				 * @see org.eclipse.ui.part.MultiPageEditorSite#getActionBarContributor()
+				 */
+				public IEditorActionBarContributor getActionBarContributor() {
+					IEditorActionBarContributor contributor = super.getActionBarContributor();
+					IEditorActionBarContributor multiContributor = XMLMultiPageEditorPart.this.getEditorSite().getActionBarContributor();
+					if (multiContributor instanceof XMLMultiPageEditorActionBarContributor) {
+						contributor = ((XMLMultiPageEditorActionBarContributor) multiContributor).sourceViewerActionContributor;
+					}
+					return contributor;
+				}
+
+				public String getId() {
+					// sets this id so nested editor is considered xml source
+					// page
+					return ContentTypeIdForXML.ContentTypeID_XML + ".source"; //$NON-NLS-1$;
+				}
+			};
+		}
+		else {
+			site = super.createSite(editor);
+		}
+		return site;
+	}
+
+	/**
+	 * Creates the source page of the multi-page editor.
+	 */
+	protected void createSourcePage() throws PartInitException {
+		fTextEditor = createTextEditor();
+		fTextEditor.setEditorPart(this);
+
+		/*
+		 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=134301 - XML editor
+		 * does not remember font settings
+		 * 
+		 * @see
+		 * org.eclipse.ui.texteditor.AbstractTextEditor#getSymbolicFontName()
+		 */
+		fPageInitializer.sendInitializationData(fTextEditor);
+
+		if (fPropertyListener == null) {
+			fPropertyListener = new PropertyListener();
+		}
+		fTextEditor.addPropertyListener(fPropertyListener);
+	}
+
+	/**
+	 * Method createTextEditor.
+	 * 
+	 * @return StructuredTextEditor
+	 */
+	private StructuredTextEditor createTextEditor() {
+		return new StructuredTextEditor();
+	}
+
+	private void disconnectDesignPage() {
+		if (fDesignViewer != null) {
+			fDesignViewer.setDocument(null);
+		}
+	}
+
+	public void dispose() {
+		Logger.trace("Source Editor", "XMLMultiPageEditorPart::dispose entry"); //$NON-NLS-1$ //$NON-NLS-2$
+
+		disconnectDesignPage();
+
+		if (fActivationListener != null) {
+			fActivationListener.dispose();
+			fActivationListener = null;
+		}
+
+		if (fMenuManager != null) {
+			fMenuManager.removeAll();
+			fMenuManager.dispose();
+			fMenuManager = null;
+		}
+
+		if ((fTextEditor != null) && (fPropertyListener != null)) {
+			fTextEditor.removePropertyListener(fPropertyListener);
+		}
+		
+		// moved to last when added window ... seems like
+		// we'd be in danger of losing some data, like site,
+		// or something.
+		super.dispose();
+
+		Logger.trace("Source Editor", "StructuredTextMultiPageEditorPart::dispose exit"); //$NON-NLS-1$ //$NON-NLS-2$
+
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	public void doSave(IProgressMonitor monitor) {
+		fTextEditor.doSave(monitor);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.ISaveablePart#doSaveAs()
+	 */
+	public void doSaveAs() {
+		fTextEditor.doSaveAs();
+		/*
+		 * Update the design viewer since the editor input would have changed
+		 * to the new file.
+		 */
+		if (fDesignViewer != null) {
+			fDesignViewer.setDocument(getDocument());
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+	 */
+	public Object getAdapter(Class key) {
+		Object result = null;
+
+		// we extend superclass, not override it, so allow it first
+		// chance to satisfy request.
+		result = super.getAdapter(key);
+
+		if (result == null) {
+			if (key == IDesignViewer.class) {
+				result = fDesignViewer;
+
+			}
+			else if (key.equals(IGotoMarker.class)) {
+				result = new IGotoMarker() {
+					public void gotoMarker(IMarker marker) {
+						XMLMultiPageEditorPart.this.gotoMarker(marker);
+					}
+				};
+			}
+			else {
+				/*
+				 * DMW: I'm bullet-proofing this because its been reported (on
+				 * very early versions) a null pointer sometimes happens here
+				 * on startup, when an editor has been left open when
+				 * workbench shutdown.
+				 */
+				if (fTextEditor != null) {
+					result = fTextEditor.getAdapter(key);
+				}
+			}
+		}
+		return result;
+	}
+
+	private IDocument getDocument() {
+		IDocument document = null;
+		if (fTextEditor != null) {
+			final IDocumentProvider provider = fTextEditor.getDocumentProvider();
+			if (provider != null) {
+				document = provider.getDocument(fTextEditor.getEditorInput());
+			}
+		}
+		return document;
+	}
+
+	private IPreferenceStore getPreferenceStore() {
+		return XMLUIPlugin.getDefault().getPreferenceStore();
+	}
+
+	StructuredTextEditor getTextEditor() {
+		return fTextEditor;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IWorkbenchPart#getTitle()
+	 */
+	public String getTitle() {
+		String title = null;
+		if (getTextEditor() == null) {
+			if (getEditorInput() != null) {
+				title = getEditorInput().getName();
+			}
+		}
+		else {
+			title = getTextEditor().getTitle();
+		}
+		if (title == null) {
+			title = getPartName();
+		}
+		return title;
+	}
+
+	void gotoMarker(IMarker marker) {
+		setActivePage(fSourcePageIndex);
+		IDE.gotoMarker(fTextEditor, marker);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite,
+	 * org.eclipse.ui.IEditorInput)
+	 */
+	public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+		try {
+			super.init(site, input);
+			// we want to listen for our own activation
+			fActivationListener = new ActivationListener(site.getWorkbenchWindow().getPartService());
+		}
+		catch (Exception e) {
+			Logger.logException("exception initializing " + getClass().getName(), e); //$NON-NLS-1$
+		}
+		setPartName(input.getName());
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed()
+	 */
+	public boolean isSaveAsAllowed() {
+		return (fTextEditor != null) && fTextEditor.isSaveAsAllowed();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.ISaveablePart#isSaveOnCloseNeeded()
+	 */
+	public boolean isSaveOnCloseNeeded() {
+		// overriding super class since it does a lowly isDirty!
+		if (fTextEditor != null) {
+			return fTextEditor.isSaveOnCloseNeeded();
+		}
+		return isDirty();
+	}
+	
+	/**
+	 * Prevents the creation of the in-editor toolbar, if called before
+	 * createPageContainer() during editor initialization.
+	 */
+	protected final void noToolbar() {
+		fAllocateToolbar = false;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.part.MultiPageEditorPart#pageChange(int)
+	 */
+	protected void pageChange(int newPageIndex) {
+		if (newPageIndex == fSourcePageIndex) {
+			ISelectionProvider provider = fDesignViewer.getSelectionProvider();
+			if (provider != null) {
+				getTextEditor().getSelectionProvider().setSelection(provider.getSelection());
+			}
+		}
+		super.pageChange(newPageIndex);
+		saveLastActivePageIndex(newPageIndex);
+
+		if (newPageIndex == fDesignPageIndex) {
+			// design page isn't an IEditorPart, therefore we have to send
+			// selection changes ourselves
+			ISelectionProvider selectionProvider = fDesignViewer.getSelectionProvider();
+			if (selectionProvider != null) {
+				SelectionChangedEvent event = new SelectionChangedEvent(selectionProvider, selectionProvider.getSelection());
+				((MultiPageSelectionProvider) getSite().getSelectionProvider()).fireSelectionChanged(event);
+				((MultiPageSelectionProvider) getSite().getSelectionProvider()).firePostSelectionChanged(event);
+			}
+		}
+	}
+
+	private void saveLastActivePageIndex(int newPageIndex) {
+		// save the last active page index to preference manager
+		getPreferenceStore().setValue(getEditorSite().getId() + "." + IXMLPreferenceNames.LAST_ACTIVE_PAGE, newPageIndex); //$NON-NLS-1$
+	}
+
+	public void setFocus() {
+		super.setFocus();
+		Control control = fDesignViewer.getControl();
+		control.setFocus();
+		// 271382 - Focus not set properly after activating XML editor
+		control.forceFocus();
+	}
+
+	public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
+		super.setInitializationData(cfig, propertyName, data);
+		fPageInitializer = new PageInitializationData(cfig, propertyName, data);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.part.EditorPart#setInput(org.eclipse.ui.IEditorInput)
+	 */
+	protected void setInput(IEditorInput input) {
+		/*
+		 * If driven from the Source page, it's "model" may not be up to date
+		 * with (or even exist for) the input just yet. Later notification
+		 * from the TextViewer could set us straight, although it's not
+		 * guaranteed to happen after the model has been created.
+		 */
+		super.setInput(input);
+		if (fDesignViewer != null) {
+			fDesignViewer.setDocument(getDocument());
+		}
+		setPartName(input.getName());
+	}
+
+	void updateStatusLine(ISelection selection) {
+		IStatusLineManager statusLineManager = getEditorSite().getActionBars().getStatusLineManager();
+		if (fStatusLineLabelProvider != null && statusLineManager != null) {
+			String text = null;
+			Image image = null;
+			if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
+				Object firstElement = ((IStructuredSelection) selection).getFirstElement();
+				if (firstElement != null) {
+					text = fStatusLineLabelProvider.getText(firstElement);
+					image = fStatusLineLabelProvider.getImage((firstElement));
+				}
+			}
+			if (image == null) {
+				statusLineManager.setMessage(text);
+			}
+			else {
+				statusLineManager.setMessage(image, text);
+			}
+		}
+	}
+
+	public INavigationLocation createEmptyNavigationLocation() {
+		if (getActivePage() == fDesignPageIndex) {
+			return new DesignPageNavigationLocation(this, fDesignViewer, false);
+		}
+		// Makes sure that the text editor is returned
+		return new TextSelectionNavigationLocation(fTextEditor, false) {
+			protected IEditorPart getEditorPart() {
+				IEditorPart part = super.getEditorPart();
+				if (part != null) {
+					part = (ITextEditor) part.getAdapter(ITextEditor.class);
+				}
+				return part;
+			}
+		};
+	}
+
+	public INavigationLocation createNavigationLocation() {
+		if (getActivePage() == fDesignPageIndex) {
+			return new DesignPageNavigationLocation(this, fDesignViewer, true);
+		}
+		// Makes sure that the text editor is returned
+		return new TextSelectionNavigationLocation(fTextEditor, true) {
+			protected IEditorPart getEditorPart() {
+				IEditorPart part = super.getEditorPart();
+				if (part != null) {
+					part = (ITextEditor) part.getAdapter(ITextEditor.class);
+				}
+				return part;
+			}
+		};
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.wst.xml.ui/src-multipage/org/eclipse/wst/xml/ui/internal/tabletree/XMLTableTreeViewer.java b/bundles/org.eclipse.wst.xml.ui/src-multipage/org/eclipse/wst/xml/ui/internal/tabletree/XMLTableTreeViewer.java
new file mode 100644
index 0000000..e1fec7b
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.ui/src-multipage/org/eclipse/wst/xml/ui/internal/tabletree/XMLTableTreeViewer.java
@@ -0,0 +1,468 @@
+/*****************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others. All rights reserved. This
+ * program and the accompanying materials are made available under the terms
+ * of the Eclipse Public License v1.0 which accompanies this distribution, and
+ * is available at http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors: IBM Corporation - initial API and implementation
+ ****************************************************************************/
+package org.eclipse.wst.xml.ui.internal.tabletree;
+
+import java.util.Iterator;
+
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.util.LocalSelectionTransfer;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ICellEditorListener;
+import org.eclipse.jface.viewers.ICellModifier;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DragSource;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.DropTargetListener;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.views.properties.IPropertyDescriptor;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+import org.eclipse.wst.xml.ui.internal.actions.NodeAction;
+import org.eclipse.wst.xml.ui.internal.contentoutline.XMLNodeActionManager;
+import org.eclipse.wst.xml.ui.internal.dnd.DragNodeCommand;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+public class XMLTableTreeViewer extends TreeViewer implements IDesignViewer {
+
+	class NodeActionMenuListener implements IMenuListener {
+		public void menuAboutToShow(IMenuManager menuManager) {
+			// used to disable NodeSelection listening while running
+			// NodeAction
+			XMLNodeActionManager nodeActionManager = new XMLNodeActionManager(((IDOMDocument) getInput()).getModel(), XMLTableTreeViewer.this) {
+				public void beginNodeAction(NodeAction action) {
+					super.beginNodeAction(action);
+				}
+
+				public void endNodeAction(NodeAction action) {
+					super.endNodeAction(action);
+				}
+			};
+			nodeActionManager.fillContextMenu(menuManager, getSelection());
+		}
+	}
+	
+	private class SelectionProvider implements IPostSelectionProvider {
+
+		public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
+			XMLTableTreeViewer.this.addPostSelectionChangedListener(listener);
+		}
+
+		public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
+			XMLTableTreeViewer.this.removePostSelectionChangedListener(listener);
+		}
+
+		public void addSelectionChangedListener(ISelectionChangedListener listener) {
+			XMLTableTreeViewer.this.addSelectionChangedListener(listener);
+		}
+
+		public ISelection getSelection() {
+			return XMLTableTreeViewer.this.getSelection();
+		}
+
+		public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+			XMLTableTreeViewer.this.removeSelectionChangedListener(listener);
+		}
+
+		public void setSelection(ISelection selection) {
+			boolean selectionSet = false;
+			if (!selection.isEmpty() && selection instanceof IStructuredSelection) {
+				IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+				if (selection instanceof ITextSelection) {
+					ITextSelection textSelection = (ITextSelection) selection;
+					
+					if (structuredSelection.size() == 1) {
+						if (structuredSelection.getFirstElement() instanceof IDOMNode) {
+							IDOMNode domNode = (IDOMNode) structuredSelection.getFirstElement();
+							IStructuredDocumentRegion startStructuredDocumentRegion = domNode.getStartStructuredDocumentRegion();
+							if (startStructuredDocumentRegion != null) {
+								ITextRegion matchingRegion = startStructuredDocumentRegion.getRegionAtCharacterOffset(textSelection.getOffset());
+								int allowedIterations = 40;
+								while (matchingRegion != null && !matchingRegion.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) && allowedIterations > 0) {
+									matchingRegion = startStructuredDocumentRegion.getRegionAtCharacterOffset(startStructuredDocumentRegion.getStartOffset(matchingRegion) - 1);
+									allowedIterations--;
+								}
+								if (matchingRegion != null && matchingRegion.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
+									String attrName = startStructuredDocumentRegion.getText(matchingRegion);
+									NamedNodeMap attributes = domNode.getAttributes();
+									if (attributes != null && attrName.length() > 0) {
+										Node attr = attributes.getNamedItem(attrName);
+										if (attr != null) {
+											selectionSet = true;
+											XMLTableTreeViewer.this.setSelection(new StructuredSelection(attr));
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+			if (!selectionSet) {
+				XMLTableTreeViewer.this.setSelection(selection);
+			}
+		}
+	}
+
+	private PaintListener fContentPaintListener = new PaintListener() {
+
+		public void paintControl(PaintEvent e) {
+			GC gc = e.gc;
+			if (getTree().getItemCount() == 0) {
+				gc.setForeground(getTree().getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND));
+				gc.setBackground(getTree().getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+				gc.drawString(XMLEditorMessages.XMLTreeExtension_3, 10, 10);
+				gc.drawString(XMLEditorMessages.XMLTreeExtension_4, 10, 10 + gc.getFontMetrics().getHeight());
+			}
+		}
+		
+	};
+
+	private ISelectionProvider fSelectionProvider = new SelectionProvider();
+	// node: 40%, content: 60%
+	static final double[] WEIGHTS = { .4, .6 };
+
+	private ControlAdapter fResizeAdapter = new ControlAdapter() {
+		public void controlResized(ControlEvent e) {
+			sizeColumns(getTree().getSize().x, WEIGHTS);
+		}
+
+		private void sizeColumns(int width, double[] weights) {
+			TreeColumn[] columns = getTree().getColumns();
+			int size = columns.length;
+			for (int i = 0; i < size; i++) {
+				columns[i].setWidth((int)(weights[i] * width));
+			}
+		}
+	};
+
+	public XMLTableTreeViewer(Composite parent) {
+		super(parent, SWT.FULL_SELECTION | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+		
+		TreeColumn column = new TreeColumn(this.getTree(), SWT.LEFT);
+		column.setText(XMLEditorMessages.XMLTableTreeViewer_1);
+
+		column = new TreeColumn(this.getTree(), SWT.LEFT);
+		column.setText(XMLEditorMessages.XMLTableTreeViewer_2);
+
+		this.getTree().setHeaderVisible(true);
+		this.getTree().setLinesVisible(true);
+		getTree().addControlListener(fResizeAdapter);
+
+		// set up providers
+		propertyDescriptorFactory = new XMLTableTreePropertyDescriptorFactory();
+
+		XMLTableTreeContentProvider provider = new XMLTableTreeContentProvider();
+		setContentProvider(provider);
+		setLabelProvider(provider);
+
+		setColumnProperties(new String[] {STRUCTURE_PROPERTY, VALUE_PROPERTY});
+		setCellEditors(new CellEditor[] {null, new TextCellEditor(this.getTree())});
+
+		setCellModifier(new XMLCMCellModifier());
+		createContextMenu();
+
+		DragSource dragSource = new DragSource(getControl(), DND.DROP_COPY | DND.DROP_MOVE);
+		dragSource.addDragListener(createDragSourceListener());
+		dragSource.setTransfer(new Transfer[] {LocalSelectionTransfer.getTransfer()});
+		DropTarget dropTarget = new DropTarget(getControl(), DND.DROP_COPY | DND.DROP_MOVE);
+		dropTarget.addDropListener(createDropTargetListener());
+		dropTarget.setTransfer(new Transfer[] {LocalSelectionTransfer.getTransfer()});
+
+		this.getTree().addPaintListener(fContentPaintListener);
+	}
+
+	/**
+	 * This creates a context menu for the viewer and adds a listener as well
+	 * registering the menu for extension.
+	 */
+	protected void createContextMenu() {
+		MenuManager contextMenu = new MenuManager("#PopUp"); //$NON-NLS-1$
+		contextMenu.add(new Separator("additions")); //$NON-NLS-1$
+		contextMenu.setRemoveAllWhenShown(true);
+		contextMenu.addMenuListener(new NodeActionMenuListener());
+		Menu menu = contextMenu.createContextMenu(getControl());
+		getControl().setMenu(menu);
+	}
+
+	private DragSourceListener createDragSourceListener() {
+		return new DragSourceListener() {
+			public void dragFinished(DragSourceEvent event) {
+				LocalSelectionTransfer.getTransfer().setSelection(null);
+			}
+
+			public void dragSetData(DragSourceEvent event) {
+			}
+
+			public void dragStart(DragSourceEvent event) {
+				LocalSelectionTransfer.getTransfer().setSelection(getSelection());
+			}
+		};
+	}
+
+	private DropTargetListener createDropTargetListener() {
+		return new DropTargetListener() {
+			public void dragEnter(DropTargetEvent event) {
+			}
+
+			public void dragLeave(DropTargetEvent event) {
+			}
+
+			public void dragOperationChanged(DropTargetEvent event) {
+			}
+
+			public void drop(DropTargetEvent event) {
+				if (event.operations != DND.DROP_NONE && LocalSelectionTransfer.getTransfer().getSelection() instanceof IStructuredSelection) {
+					dragOver(event);
+					IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection();
+					if (selection != null && !selection.isEmpty() && event.item != null && event.item.getData() != null) {
+						/*
+						 * the command uses these numbers instead of the
+						 * feedback constants (even though it converts in
+						 * the other direction as well)
+						 */
+						float feedbackFloat = getHeightInItem(event);
+
+						final DragNodeCommand command = new DragNodeCommand(event.item.getData(), feedbackFloat, event.operations, event.detail, selection.toList(), XMLTableTreeViewer.this);
+						if (command != null && command.canExecute()) {
+							SafeRunnable.run(new SafeRunnable() {
+								public void run() throws Exception {
+									command.execute();
+								}
+							});
+						}
+					}
+				}
+			}
+
+			public void dropAccept(DropTargetEvent event) {
+			}
+
+			public void dragOver(DropTargetEvent event) {
+				event.feedback = DND.FEEDBACK_SELECT;
+				float feedbackFloat = getHeightInItem(event);
+				if (feedbackFloat > 0.75) {
+					event.feedback = DND.FEEDBACK_INSERT_AFTER;
+				}
+				else if (feedbackFloat < 0.25) {
+					event.feedback = DND.FEEDBACK_INSERT_BEFORE;
+				}
+				event.feedback |= DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
+				if ( getLocalNodeSelection(event) == null){
+					event.detail = DND.DROP_NONE;
+				}
+			}
+
+			private IStructuredSelection getLocalNodeSelection(DropTargetEvent event){
+				IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection();
+				if (selection != null && !selection.isEmpty() ) {
+					for (Iterator i = selection.iterator(); i.hasNext();) {
+						Object node = i.next();
+						if (node instanceof Node){
+							return selection;
+						}
+					}
+				}
+				return null;
+			}
+			private float getHeightInItem(DropTargetEvent event) {
+				if(event.item == null) return .5f;
+				if (event.item instanceof TreeItem) {
+					TreeItem treeItem = (TreeItem) event.item;
+					Control control = treeItem.getParent();
+					Point point = control.toControl(new Point(event.x, event.y));
+					Rectangle bounds = treeItem.getBounds();
+					return (float) (point.y - bounds.y) / (float) bounds.height;
+				}
+				else if (event.item instanceof TableItem) {
+					TableItem tableItem = (TableItem) event.item;
+					Control control = tableItem.getParent();
+					Point point = control.toControl(new Point(event.x, event.y));
+					Rectangle bounds = tableItem.getBounds(0);
+					return (float) (point.y - bounds.y) / (float) bounds.height;
+				}
+				else {
+					return 0.0F;
+				}
+			}
+		};
+	}
+	
+	protected void doRefresh(Object o, boolean fromDelayed) {
+		super.refresh(o);
+	}
+
+	public ISelectionProvider getSelectionProvider() {
+		return fSelectionProvider;
+	}
+
+	public String getTitle() {
+		return XMLEditorMessages.XMLTableTreeViewer_0;
+	}
+
+	protected void handleDispose(DisposeEvent event) {
+		super.handleDispose(event);
+		this.getTree().removePaintListener(fContentPaintListener);
+		getTree().removeControlListener(fResizeAdapter);
+		setDocument(null);
+	}
+
+	public void setDocument(IDocument document) {
+		/*
+		 * let the text editor to be the one that manages the model's lifetime
+		 */
+		IStructuredModel model = null;
+		try {
+			model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+
+			if ((model != null) && (model instanceof IDOMModel)) {
+				Document domDoc = null;
+				domDoc = ((IDOMModel) model).getDocument();
+				setInput(domDoc);
+			}
+		}
+		finally {
+			if (model != null) {
+				model.releaseFromRead();
+			}
+		}
+
+	}
+	
+	protected TreeContentHelper treeContentHelper = new TreeContentHelper();
+	protected XMLTableTreePropertyDescriptorFactory propertyDescriptorFactory;
+	
+	private final static String STRUCTURE_PROPERTY = XMLEditorMessages.XMLTreeExtension_0;
+	private final static String VALUE_PROPERTY = XMLEditorMessages.XMLTreeExtension_1;
+	
+	private class CellListener implements ICellEditorListener {
+
+		private Node node;
+		private CellEditor editor;
+		private String originalValue;
+
+		CellListener(Node data, CellEditor editor) {
+			this.node = data;
+			this.editor = editor;
+			
+			((IDOMNode) node).getModel().aboutToChangeModel();
+			originalValue = treeContentHelper.getNodeValue(node);
+		}
+
+		public void applyEditorValue() {
+			((IDOMNode) node).getModel().changedModel();
+			editor.removeListener(this);
+		}
+
+		public void cancelEditor() {
+			final Object value = editor.getValue();
+			if (value != null && !value.equals(originalValue)) {
+				treeContentHelper.setNodeValue(node, originalValue);
+			}
+			((IDOMNode) node).getModel().changedModel();
+			editor.removeListener(this);
+		}
+
+		public void editorValueChanged(boolean oldValidState, boolean newValidState) {
+			if (newValidState) {
+				treeContentHelper.setNodeValue(node, editor.getValue().toString(), getControl().getShell());
+			}
+		}
+		
+	}
+
+	public class XMLCMCellModifier implements ICellModifier, TreeExtension.ICellEditorProvider {
+		public boolean canModify(Object element, String property) {
+			boolean result = false;
+			if (element instanceof Node) {
+				Node node = (Node) element;
+				if (property == VALUE_PROPERTY) {
+					result = treeContentHelper.isEditable(node);
+					if (result) {
+						/* Set up the cell editor based on the element */
+						CellEditor[] editors = getCellEditors();
+						if (editors.length > 0) {
+							if (editors[1] != null)
+								editors[1].dispose();
+							editors[1] = getCellEditor(element, 1);
+							if (editors[1] instanceof TextCellEditor) {
+								editors[1].addListener(new CellListener(node, editors[1]));
+							}
+						}
+					}
+					
+				}
+			}
+			return result;
+		}
+
+		public Object getValue(Object object, String property) {
+			String result = null;
+			if (object instanceof Node) {
+				result = treeContentHelper.getNodeValue((Node) object);
+			}
+			return (result != null) ? result : ""; //$NON-NLS-1$
+		}
+
+		public void modify(Object element, String property, Object value) {
+			Item item = (Item) element;
+			String oldValue = treeContentHelper.getNodeValue((Node) item.getData());
+			String newValue = value.toString();
+			if ((newValue != null) && !newValue.equals(oldValue)) {
+				treeContentHelper.setNodeValue((Node) item.getData(), value.toString(), getControl().getShell());
+			}
+		}
+
+		public CellEditor getCellEditor(Object o, int col) {
+			IPropertyDescriptor pd = propertyDescriptorFactory.createPropertyDescriptor(o);
+			return pd != null ? pd.createPropertyEditor(XMLTableTreeViewer.this.getTree()) : null;
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.wst.xml.ui/src-wizards/org/eclipse/wst/xml/ui/internal/wizards/NewXMLGenerator.java b/bundles/org.eclipse.wst.xml.ui/src-wizards/org/eclipse/wst/xml/ui/internal/wizards/NewXMLGenerator.java
new file mode 100644
index 0000000..476a754
--- /dev/null
+++ b/bundles/org.eclipse.wst.xml.ui/src-wizards/org/eclipse/wst/xml/ui/internal/wizards/NewXMLGenerator.java
@@ -0,0 +1,422 @@
+/*******************************************************************************
+ * Copyright (c) 2001, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wst.xml.ui.internal.wizards;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.util.Assert;
+import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolver;
+import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverPlugin;
+import org.eclipse.wst.common.uriresolver.internal.util.URIHelper;
+import org.eclipse.wst.sse.core.internal.encoding.CommonEncodingPreferenceNames;
+import org.eclipse.wst.sse.core.utils.StringUtils;
+import org.eclipse.wst.xml.core.internal.XMLCorePlugin;
+import org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogEntry;
+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.CMNamedNodeMap;
+import org.eclipse.wst.xml.core.internal.contentmodel.ContentModelManager;
+import org.eclipse.wst.xml.core.internal.contentmodel.util.ContentBuilder;
+import org.eclipse.wst.xml.core.internal.contentmodel.util.DOMContentBuilderImpl;
+import org.eclipse.wst.xml.core.internal.contentmodel.util.DOMWriter;
+import org.eclipse.wst.xml.core.internal.contentmodel.util.NamespaceInfo;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class NewXMLGenerator {
+
+	protected String grammarURI;
+	protected CMDocument cmDocument;
+	protected int buildPolicy;
+	protected String rootElementName;
+
+	protected ICatalogEntry xmlCatalogEntry;
+	protected int optionalElementDepthLimit = -1;
+
+	// info for dtd
+	protected String publicId;
+	protected String systemId;
+	protected String defaultSystemId;
+
+	// info for xsd
+	public List namespaceInfoList;
+
+	public NewXMLGenerator() {
+		super();
+	}
+
+	public NewXMLGenerator(String grammarURI, CMDocument cmDocument) {
+		this.grammarURI = grammarURI;
+		this.cmDocument = cmDocument;
+	}
+
+
+	public static CMDocument createCMDocument(String uri, String[] errorInfo) {
+		String title = null;
+		String message = null;
+		List errorList = new Vector();
+		CMDocument cmDocument = null;
+
+		if (URIHelper.isReadableURI(uri, true)) {
+			// (cs) assume the uri has been provided in a normalized form
+			cmDocument = ContentModelManager.getInstance().createCMDocument(uri, null);
+
+			if (uri.endsWith(".dtd")) { //$NON-NLS-1$
+				if (errorList.size() > 0) {
+					title = XMLWizardsMessages._UI_INVALID_GRAMMAR_ERROR;
+					message = XMLWizardsMessages._UI_LABEL_ERROR_DTD_INVALID_INFO;
+				}
+			}
+			else // ".xsd"
+			{
+				// To be consistent with the schema editor validation
+				XMLSchemaValidationChecker validator = new XMLSchemaValidationChecker();
+				if (!validator.isValid(uri)) {
+					title = XMLWizardsMessages._UI_INVALID_GRAMMAR_ERROR;
+					message = XMLWizardsMessages._UI_LABEL_ERROR_SCHEMA_INVALID_INFO;
+				}
+				else if (cmDocument != null) {
+					int globalElementCount = cmDocument.getElements().getLength();
+					if (globalElementCount == 0) {
+						title = XMLWizardsMessages._UI_WARNING_TITLE_NO_ROOT_ELEMENTS;
+						message = XMLWizardsMessages._UI_WARNING_MSG_NO_ROOT_ELEMENTS;
+					}
+				}
+			}
+		}
+		else {
+			title = XMLWizardsMessages._UI_WARNING_TITLE_NO_ROOT_ELEMENTS;
+			message = XMLWizardsMessages._UI_WARNING_URI_NOT_FOUND_COLON + " " + uri; //$NON-NLS-1$
+		}
+		errorInfo[0] = title;
+		errorInfo[1] = message;
+
+		return cmDocument;
+	}
+	
+	private String applyLineDelimiter(IFile file, String text) {
+		String systemLineSeparator = System.getProperty("line.separator");
+		String lineDelimiter = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, systemLineSeparator, new IScopeContext[] {new ProjectScope(file.getProject()), new InstanceScope() });//$NON-NLS-1$
+		if(!systemLineSeparator.equals(lineDelimiter)) {
+			String convertedText = StringUtils.replace(text, "\r\n", "\n");
+			convertedText = StringUtils.replace(convertedText, "\r", "\n");
+			convertedText = StringUtils.replace(convertedText, "\n", lineDelimiter);
+			return convertedText;
+		}
+		return text;
+	}
+
+	/**
+	 * @deprecated use createTemplateXMLDocument(IFile, String) instead
+	 */
+	public void createEmptyXMLDocument(IFile newFile) throws Exception {
+		String charSet = getUserPreferredCharset();
+		String contents = "<?xml version=\"1.0\" encoding=\"" + charSet + "\"?>"; //$NON-NLS-1$ //$NON-NLS-2$
+		createTemplateXMLDocument(newFile, contents);
+	}
+	
+	void createTemplateXMLDocument(IFile newFile, String contents) throws Exception {
+		if (contents != null) {
+			String charSet = getUserPreferredCharset();
+			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+	
+			PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream, charSet));
+			contents = applyLineDelimiter(newFile, contents);
+			writer.println(contents);
+			writer.flush();
+			outputStream.close();
+	
+			ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+			newFile.setContents(inputStream, true, true, null);
+			inputStream.close();
+		}
+	}
+
+	private String getUserPreferredCharset() {
+		Preferences preference = XMLCorePlugin.getDefault().getPluginPreferences();
+		String charSet = preference.getString(CommonEncodingPreferenceNames.OUTPUT_CODESET);
+		return charSet;
+	}
+
+	public void createXMLDocument(String xmlFileName) throws Exception {
+		IContentType contentType = Platform.getContentTypeManager().findContentTypeFor(xmlFileName);
+		String charset = null;
+		if (contentType != null) {
+			charset = contentType.getDefaultCharset();
+		}
+		ByteArrayOutputStream outputStream = createXMLDocument(xmlFileName, charset);
+
+		File file = new File(xmlFileName);
+		FileOutputStream fos = new FileOutputStream(file);
+		outputStream.writeTo(fos);
+		fos.close();
+	}
+
+
+	public void createXMLDocument(IFile newFile, String xmlFileName) throws Exception {
+		String charset = newFile.getCharset();
+		ByteArrayOutputStream outputStream = createXMLDocument(xmlFileName, charset);
+
+		String contents = outputStream.toString(charset);
+		contents = applyLineDelimiter(newFile, contents);
+
+		ByteArrayInputStream inputStream = new ByteArrayInputStream(contents.getBytes(charset));
+		newFile.setContents(inputStream, true, true, null);
+		inputStream.close();
+	}
+
+
+	public ByteArrayOutputStream createXMLDocument(String xmlFileName, String charset) throws Exception {
+		if (charset == null) {
+			charset = getUserPreferredCharset();
+			if (charset == null) {
+				charset = "UTF-8"; //$NON-NLS-1$
+			}
+		}
+		CMDocument cmDocument = getCMDocument();
+
+		Assert.isNotNull(cmDocument);
+		Assert.isNotNull(getRootElementName());
+
+		// create the xml model
+		CMNamedNodeMap nameNodeMap = cmDocument.getElements();
+		CMElementDeclaration cmElementDeclaration = (CMElementDeclaration) nameNodeMap.getNamedItem(getRootElementName());
+
+		Document xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+		DOMContentBuilderImpl contentBuilder = new DOMContentBuilderImpl(xmlDocument);
+
+		// this 'uglyTempHack' flag is required in order to supress the
+		// creation a default encoding
+		// we'll handle this later in the domWriter.print() method used below
+		//
+		contentBuilder.supressCreationOfDoctypeAndXMLDeclaration = true;
+		contentBuilder.setBuildPolicy(buildPolicy);
+		contentBuilder.setOptionalElementDepthLimit(optionalElementDepthLimit);
+		contentBuilder.setExternalCMDocumentSupport(new MyExternalCMDocumentSupport(namespaceInfoList, xmlFileName));
+		contentBuilder.createDefaultRootContent(cmDocument, cmElementDeclaration, namespaceInfoList);
+
+		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+		OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, charset);
+
+		DOMWriter domWriter = new DOMWriter(outputStreamWriter);
+
+		// TODO... instead of relying on file extensions, we need to keep
+		// track of the grammar type
+		// better yet we should reate an SSE document so that we can format it
+		// nicely before saving
+		// then we won't need the DOMWriter at all
+		//
+		domWriter.print(xmlDocument, charset, cmDocument.getNodeName(), getNonWhitespaceString(getPublicId()), getNonWhitespaceString(getSystemId()));
+		outputStream.flush();
+		outputStream.close();
+
+		return outputStream;
+	}
+
+
+	public void createNamespaceInfoList() {
+		List result = new Vector();
+		if (cmDocument != null) {
+			result = (List) cmDocument.getProperty("http://org.eclipse.wst/cm/properties/completeNamespaceInfo"); //$NON-NLS-1$
+			if (result != null) {
+				int size = result.size();
+				for (int i = 0; i < size; i++) {
+					NamespaceInfo info = (NamespaceInfo) result.get(i);
+					if (i == 0) {
+						String locationInfo = null;
+						if (xmlCatalogEntry != null) {
+							if (xmlCatalogEntry.getEntryType() == ICatalogEntry.ENTRY_TYPE_PUBLIC) {
+								locationInfo = xmlCatalogEntry.getAttributeValue(ICatalogEntry.ATTR_WEB_URL);
+							}
+							else {
+								locationInfo = xmlCatalogEntry.getKey();
+							}
+						}
+						if (locationInfo == null) {
+							locationInfo = defaultSystemId;
+						}
+						info.locationHint = locationInfo;
+						info.setProperty("locationHint-readOnly", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+						info.setProperty("uri-readOnly", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+						info.setProperty("unremovable", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+					} else {
+						info.locationHint = null;
+					}
+				}
+			}
+
+			NamespaceInfoContentBuilder builder = new NamespaceInfoContentBuilder();
+			builder.setBuildPolicy(ContentBuilder.BUILD_ONLY_REQUIRED_CONTENT);
+			builder.visitCMNode(cmDocument);
+			result.addAll(builder.list);
+		}
+		namespaceInfoList = result;
+	}
+
+
+	public boolean isMissingNamespaceLocation() {
+		boolean result = false;
+		for (Iterator i = namespaceInfoList.iterator(); i.hasNext();) {
+			NamespaceInfo info = (NamespaceInfo) i.next();
+			if (info.locationHint == null) {
+				result = true;
+				break;
+			}
+		}
+		return result;
+	}
+
+
+	public String[] getNamespaceInfoErrors() {
+		String[] errorList = null;
+
+// No warnings should be given when namespaces entries have missing location hints
+// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=105128		
+//		if ((namespaceInfoList != null) && isMissingNamespaceLocation()) {
+//			String title = XMLWizardsMessages._UI_LABEL_NO_LOCATION_HINT;
+//			String message = XMLWizardsMessages._UI_WARNING_MSG_NO_LOCATION_HINT_1 + " " + XMLWizardsMessages._UI_WARNING_MSG_NO_LOCATION_HINT_2 + "\n\n" + XMLWizardsMessages._UI_WARNING_MSG_NO_LOCATION_HINT_3; //$NON-NLS-1$ //$NON-NLS-2$
+//
+//			errorList = new String[2];
+//			errorList[0] = title;
+//			errorList[1] = message;
+//		}
+		return errorList;
+	}
+
+
+	public void setXMLCatalogEntry(ICatalogEntry catalogEntry) {
+		xmlCatalogEntry = catalogEntry;
+	}
+
+	public ICatalogEntry getXMLCatalogEntry() {
+		return xmlCatalogEntry;
+	}
+
+
+	public void setBuildPolicy(int policy) {
+		buildPolicy = policy;
+	}
+
+
+	public void setDefaultSystemId(String sysId) {
+		defaultSystemId = sysId;
+	}
+
+	public String getDefaultSystemId() {
+		return defaultSystemId;
+	}
+
+	public void setSystemId(String sysId) {
+		systemId = sysId;
+	}
+
+	public String getSystemId() {
+		return systemId;
+	}
+
+	public void setPublicId(String pubId) {
+		publicId = pubId;
+	}
+
+	public String getPublicId() {
+		return publicId;
+	}
+
+	public void setGrammarURI(String gramURI) {
+		grammarURI = gramURI;
+	}
+
+	public String getGrammarURI() {
+		return grammarURI;
+	}
+
+	public void setCMDocument(CMDocument cmDoc) {
+		cmDocument = cmDoc;
+	}
+
+	public CMDocument getCMDocument() {
+		return cmDocument;
+	}
+
+	public void setRootElementName(String rootName) {
+		rootElementName = rootName;
+	}
+
+	public String getRootElementName() {
+		return rootElementName;
+	}
+
+
+	protected class MyExternalCMDocumentSupport implements DOMContentBuilderImpl.ExternalCMDocumentSupport {
+		protected List namespaceInfoList1;
+		protected URIResolver idResolver;
+		protected String resourceLocation;
+
+		protected MyExternalCMDocumentSupport(List namespaceInfoListParam, String resourceLocation) {
+			this.namespaceInfoList1 = namespaceInfoListParam;
+			this.resourceLocation = resourceLocation;
+			idResolver = URIResolverPlugin.createResolver();
+		}
+
+		public CMDocument getCMDocument(Element element, String namespaceURI) {
+			CMDocument result = null;
+			if ((namespaceURI != null) && (namespaceURI.trim().length() > 0)) {
+				String locationHint = null;
+				for (Iterator i = namespaceInfoList1.iterator(); i.hasNext();) {
+					NamespaceInfo info = (NamespaceInfo) i.next();
+					if (namespaceURI.equals(info.uri)) {
+						locationHint = info.locationHint;
+						break;
+					}
+				}
+				if (locationHint != null) {
+					grammarURI = idResolver.resolve(resourceLocation, locationHint, locationHint);
+					result = ContentModelManager.getInstance().createCMDocument(getGrammarURI(), null);
+				}
+			}
+			else {
+				result = cmDocument;
+			}
+			return result;
+		}
+	}
+
+	public static String getNonWhitespaceString(String string) {
+		String result = null;
+		if (string != null) {
+			if (string.trim().length() > 0) {
+				result = string;
+			}
+		}
+		return result;
+	}
+	
+	public void setOptionalElementDepthLimit(int optionalElementDepthLimit) {
+		this.optionalElementDepthLimit = optionalElementDepthLimit;
+	}
+}
diff --git a/docs/org.eclipse.wst.web.ui.infopop/.cvsignore b/docs/org.eclipse.wst.web.ui.infopop/.cvsignore
new file mode 100644
index 0000000..c14487c
--- /dev/null
+++ b/docs/org.eclipse.wst.web.ui.infopop/.cvsignore
@@ -0,0 +1 @@
+build.xml
diff --git a/docs/org.eclipse.wst.web.ui.infopop/.project b/docs/org.eclipse.wst.web.ui.infopop/.project
new file mode 100644
index 0000000..10c4a6e
--- /dev/null
+++ b/docs/org.eclipse.wst.web.ui.infopop/.project
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.wst.web.ui.infopop</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+	</buildSpec>
+	<natures>
+	</natures>
+</projectDescription>
diff --git a/docs/org.eclipse.wst.web.ui.infopop/META-INF/MANIFEST.MF b/docs/org.eclipse.wst.web.ui.infopop/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..8a60c52
--- /dev/null
+++ b/docs/org.eclipse.wst.web.ui.infopop/META-INF/MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Static Web infopop
+Bundle-SymbolicName: org.eclipse.wst.web.ui.infopop; singleton:=true
+Bundle-Version: 1.0.300.qualifier
+Bundle-Vendor: %pluginProvider
+Bundle-Localization: plugin
+Eclipse-AutoStart: true
diff --git a/docs/org.eclipse.wst.web.ui.infopop/StaticWebWizContexts.xml b/docs/org.eclipse.wst.web.ui.infopop/StaticWebWizContexts.xml
new file mode 100644
index 0000000..bf0f403
--- /dev/null
+++ b/docs/org.eclipse.wst.web.ui.infopop/StaticWebWizContexts.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?NLS type="org.eclipse.help.contexts"?>
+<!-- /*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/ -->
+<contexts>
+<context id="webw2000">
+<description> Use this page to name your Web project and specify the file system location (the place where the resources you create are stored.) When the Use default check box is selected, the project will be created in the file system location where your workspace resides. To change the default file system location, clear the checkbox and locate the path using the <b>Browse</b> button. To configure additional options, select the <b>Next</b> button.
+In the Target Runtime field, select the server where you want to publish the Web project. if a server is not already defined, click <b>New</b> to select a server runtime environment. </description>
+<topic label="Creating a static Web project" href="../org.eclipse.wst.webtools.doc.user/topics/twcresta.html"/>
+</context>
+
+<context id="webw2100">
+<description>Presets are used to define a default set of facet versions that will configure a project for a particular type of development. The Static Web Module facet enables the project to be deployed as a static
+Web module. Click Show Runtimes to view the available runtimes and runtime compositions.</description>
+<topic label="Creating a static Web project" href="../org.eclipse.wst.webtools.doc.user/topics/twcresta.html"/>
+</context>
+
+<context id="webw2200">
+<description>The Web content folder is where the elements of your Web site such as Web pages, graphics and style sheets are stored. This directory structure is necessary to ensure that the content of your Web site will be included in the WAR file at deployment and that link validation will work correctly.
+</description>
+<topic label="Creating a static Web project" href="../org.eclipse.wst.webtools.doc.user/topics/twcresta.html"/>
+</context>
+
+
+</contexts>
diff --git a/docs/org.eclipse.wst.web.ui.infopop/about.html b/docs/org.eclipse.wst.web.ui.infopop/about.html
new file mode 100644
index 0000000..2199df3
--- /dev/null
+++ b/docs/org.eclipse.wst.web.ui.infopop/about.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<HTML>
+
+<head>
+<title>About</title>
+<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
+</head>
+
+<BODY lang="EN-US">
+
+<H3>About This Content</H3>
+
+<P>June, 2008</P>
+
+<H3>License</H3>
+
+<P>The Eclipse Foundation makes available all content in this plug-in 
+("Content"). Unless otherwise indicated below, the Content is provided to you 
+under the terms and conditions of the Eclipse Public License Version 1.0 
+("EPL"). A copy of the EPL is available at
+<A href="http://www.eclipse.org/org/documents/epl-v10.php">http://www.eclipse.org/org/documents/epl-v10.php</A>. 
+For purposes of the EPL, "Program" will mean the Content.</P>
+
+<P>If you did not receive this Content directly from the Eclipse Foundation, the 
+Content is being redistributed by another party ("Redistributor") and different 
+terms and conditions may apply to your use of any object code in the Content. 
+Check the Redistributor’s license that was provided with the Content. If no such 
+license exists, contact the Redistributor. Unless otherwise indicated below, the 
+terms and conditions of the EPL still apply to any source code in the Content 
+and such source code may be obtained at
+<A href="http://www.eclipse.org/">http://www.eclipse.org/</A>.</P>
+
+</BODY>
+</HTML>
diff --git a/docs/org.eclipse.wst.web.ui.infopop/build.properties b/docs/org.eclipse.wst.web.ui.infopop/build.properties
new file mode 100644
index 0000000..0c7c1a9
--- /dev/null
+++ b/docs/org.eclipse.wst.web.ui.infopop/build.properties
@@ -0,0 +1,7 @@
+bin.includes = StaticWebWizContexts.xml,\
+               about.html,\
+               plugin.xml,\
+               plugin.properties,\
+               META-INF/
+src.includes = build.properties
+generateSourceBundle=false
\ No newline at end of file
diff --git a/docs/org.eclipse.wst.web.ui.infopop/plugin.properties b/docs/org.eclipse.wst.web.ui.infopop/plugin.properties
new file mode 100644
index 0000000..b61d2de
--- /dev/null
+++ b/docs/org.eclipse.wst.web.ui.infopop/plugin.properties
@@ -0,0 +1,13 @@
+###############################################################################
+# Copyright (c) 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+
+pluginName     = Static Web infopop
+pluginProvider = Eclipse.org
\ No newline at end of file
diff --git a/docs/org.eclipse.wst.web.ui.infopop/plugin.xml b/docs/org.eclipse.wst.web.ui.infopop/plugin.xml
new file mode 100644
index 0000000..20ac018
--- /dev/null
+++ b/docs/org.eclipse.wst.web.ui.infopop/plugin.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!-- ================================================= -->
+<!-- This is the plugin for declaring the help         -->
+<!-- contributions for using the tool.                 -->
+<!-- ================================================= -->
+<!-- /*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/ -->
+<plugin>
+
+<extension point="org.eclipse.help.contexts">
+      <contexts file="StaticWebWizContexts.xml" plugin ="org.eclipse.wst.web.ui"/>
+</extension>
+
+
+</plugin>
\ No newline at end of file
diff --git a/features/org.eclipse.wst.web_userdoc.feature/.cvsignore b/features/org.eclipse.wst.web_userdoc.feature/.cvsignore
new file mode 100644
index 0000000..9cfe174
--- /dev/null
+++ b/features/org.eclipse.wst.web_userdoc.feature/.cvsignore
@@ -0,0 +1,2 @@
+build.xml
+org.eclipse.wst.web_userdoc.feature_1.0.0.jar
diff --git a/features/org.eclipse.wst.web_userdoc.feature/.project b/features/org.eclipse.wst.web_userdoc.feature/.project
new file mode 100644
index 0000000..1b5eb31
--- /dev/null
+++ b/features/org.eclipse.wst.web_userdoc.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.wst.web_userdoc.feature</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.FeatureBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.FeatureNature</nature>
+	</natures>
+</projectDescription>
diff --git a/features/org.eclipse.wst.web_userdoc.feature/build.properties b/features/org.eclipse.wst.web_userdoc.feature/build.properties
new file mode 100644
index 0000000..7c626e2
--- /dev/null
+++ b/features/org.eclipse.wst.web_userdoc.feature/build.properties
@@ -0,0 +1,3 @@
+bin.includes = feature.xml,\
+               eclipse_update_120.jpg,\
+               feature.properties
diff --git a/features/org.eclipse.wst.web_userdoc.feature/eclipse_update_120.jpg b/features/org.eclipse.wst.web_userdoc.feature/eclipse_update_120.jpg
new file mode 100644
index 0000000..bfdf708
--- /dev/null
+++ b/features/org.eclipse.wst.web_userdoc.feature/eclipse_update_120.jpg
Binary files differ
diff --git a/features/org.eclipse.wst.web_userdoc.feature/feature.properties b/features/org.eclipse.wst.web_userdoc.feature/feature.properties
new file mode 100644
index 0000000..3217521
--- /dev/null
+++ b/features/org.eclipse.wst.web_userdoc.feature/feature.properties
@@ -0,0 +1,48 @@
+###############################################################################
+# Copyright (c) 2006 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# feature.properties
+# contains externalized strings for feature.xml
+# "%foo" in feature.xml corresponds to the key "foo" in this file
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# This file should be translated.
+
+# "featureName" property - name of the feature
+featureName=WST Web User Documentation
+
+# "providerName" property - name of the company that provides the feature
+providerName=Eclipse Web Tools Platform
+
+
+# "description" property - description of the feature
+description=WST Web user documentation
+
+# "copyright" property - text of the "Feature Update Copyright"
+copyright=\
+Copyright (c) 2006 IBM Corporation and others.\n\
+All rights reserved. This program and the accompanying materials\n\
+are made available under the terms of the Eclipse Public License v1.0\n\
+which accompanies this distribution, and is available at\n\
+http://www.eclipse.org/legal/epl-v10.html\n\
+\n\
+Contributors:\n\
+    IBM Corporation - initial API and implementation\n
+################ end of copyright property ####################################
+
+# "licenseURL" property - URL of the "Feature License"
+# do not translate value - just change to point to a locale-specific HTML page
+# license and licenseURL properties were removed as a result to migrating to new PDE license support. 
+#    Those properties are now added at build time. See http://wiki.eclipse.org/Equinox/p2/License_Mechanism. 
+
+# "license" property - text of the "Feature Update License"
+# should be plain text version of license agreement pointed to be "licenseURL"
+# license and licenseURL properties were removed as a result to migrating to new PDE license support. 
+#    Those properties are now added at build time. See http://wiki.eclipse.org/Equinox/p2/License_Mechanism. 
+########### end of license property ##########################################
diff --git a/features/org.eclipse.wst.web_userdoc.feature/feature.xml b/features/org.eclipse.wst.web_userdoc.feature/feature.xml
new file mode 100644
index 0000000..e3619cd
--- /dev/null
+++ b/features/org.eclipse.wst.web_userdoc.feature/feature.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="org.eclipse.wst.web_userdoc.feature"
+      label="%featureName"
+      version="3.3.0.qualifier"
+      provider-name="%providerName"
+      license-feature="org.eclipse.license"
+      license-feature-version="1.0.0.qualifier">
+
+   <description>
+      %description
+   </description>
+
+   <copyright>
+      %copyright
+   </copyright>
+
+   <license url="license.html">
+      %license
+   </license>
+
+   <plugin
+         id="org.eclipse.wst.doc.user"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.webtools.doc.user"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>
diff --git a/features/org.eclipse.wst.xml_core.feature/feature.xml b/features/org.eclipse.wst.xml_core.feature/feature.xml
new file mode 100644
index 0000000..072c087
--- /dev/null
+++ b/features/org.eclipse.wst.xml_core.feature/feature.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="org.eclipse.wst.xml_core.feature"
+      label="%featureName"
+      version="3.3.1.qualifier"
+      provider-name="%providerName"
+      license-feature="org.eclipse.license"
+      license-feature-version="1.0.0.qualifier">
+
+   <description>
+      %description
+   </description>
+
+   <copyright>
+      %copyright
+   </copyright>
+
+   <license url="%licenseURL">
+      %license
+   </license>
+
+   <plugin
+         id="javax.xml"
+         download-size="190"
+         install-size="190"
+         version="1.3.4.qualifier"
+         unpack="false"/>
+
+   <plugin
+         id="org.apache.xml.resolver"
+         download-size="96"
+         install-size="96"
+         version="1.2.0.qualifier"
+         unpack="false"/>
+
+   <plugin
+         id="org.apache.xml.serializer"
+         download-size="270"
+         install-size="270"
+         version="2.7.1.qualifier"
+         unpack="false"/>
+
+   <plugin
+         id="org.apache.xerces"
+         download-size="1200"
+         install-size="1200"
+         version="2.9.0.qualifier"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.standard.schemas"
+         download-size="152"
+         install-size="152"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.xml.core"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.dtd.core"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.xsd.core"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.sse.core"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>
diff --git a/features/org.eclipse.wst.xml_sdk.feature/feature.xml b/features/org.eclipse.wst.xml_sdk.feature/feature.xml
new file mode 100644
index 0000000..0f4a483
--- /dev/null
+++ b/features/org.eclipse.wst.xml_sdk.feature/feature.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="org.eclipse.wst.xml_sdk.feature"
+      label="%featureName"
+      version="3.3.1.qualifier"
+      provider-name="%providerName"
+      license-feature="org.eclipse.license"
+      license-feature-version="1.0.0.qualifier">
+
+   <description>
+      %description
+   </description>
+
+   <copyright>
+      %copyright
+   </copyright>
+
+   <license url="%licenseURL">
+      %license
+   </license>
+
+   <includes
+         id="org.eclipse.wst.common_sdk.feature"
+         version="0.0.0"/>
+
+   <includes
+         id="org.eclipse.wst.xml_ui.feature"
+         version="0.0.0"/>
+
+   <includes
+         id="org.eclipse.wst.xml_ui.feature.source"
+         version="0.0.0"/>
+
+</feature>
diff --git a/features/org.eclipse.wst.xml_ui.feature/feature.xml b/features/org.eclipse.wst.xml_ui.feature/feature.xml
new file mode 100644
index 0000000..2810931
--- /dev/null
+++ b/features/org.eclipse.wst.xml_ui.feature/feature.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="org.eclipse.wst.xml_ui.feature"
+      label="%featureName"
+      version="3.3.1.qualifier"
+      provider-name="%providerName"
+      plugin="org.eclipse.wst.xml.ui"
+      license-feature="org.eclipse.license"
+      license-feature-version="1.0.0.qualifier">
+
+   <description>
+      %description
+   </description>
+
+   <copyright>
+      %copyright
+   </copyright>
+
+   <license url="%licenseURL">
+      %license
+   </license>
+
+   <url>
+      <update label="%updateSiteName" url="http://download.eclipse.org/webtools/repository/indigo"/>
+   </url>
+
+   <includes
+         id="org.eclipse.wst.common_ui.feature"
+         version="0.0.0"/>
+
+   <includes
+         id="org.eclipse.wst.xml_userdoc.feature"
+         version="0.0.0"/>
+
+   <includes
+         id="org.eclipse.wst.xml_core.feature"
+         version="0.0.0"/>
+
+   <plugin
+         id="org.eclipse.wst.dtd.ui"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.dtd.ui.infopop"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.sse.ui.infopop"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.xml.ui.infopop"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.sse.ui"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.xml.ui"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.wst.xsd.ui"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>