[276702] [editor] Closing brace should come up automatically
diff --git a/bundles/org.eclipse.wst.jsdt.web.support.jsp/src/org/eclipse/wst/jsdt/web/support/jsp/JSDTStructuredTextViewerConfigurationJSP.java b/bundles/org.eclipse.wst.jsdt.web.support.jsp/src/org/eclipse/wst/jsdt/web/support/jsp/JSDTStructuredTextViewerConfigurationJSP.java
index 9502c89..661e60d 100644
--- a/bundles/org.eclipse.wst.jsdt.web.support.jsp/src/org/eclipse/wst/jsdt/web/support/jsp/JSDTStructuredTextViewerConfigurationJSP.java
+++ b/bundles/org.eclipse.wst.jsdt.web.support.jsp/src/org/eclipse/wst/jsdt/web/support/jsp/JSDTStructuredTextViewerConfigurationJSP.java
@@ -13,11 +13,13 @@
  */
 package org.eclipse.wst.jsdt.web.support.jsp;
 
+import org.eclipse.jface.text.IAutoEditStrategy;
 import org.eclipse.jface.text.source.ISourceViewer;
 import org.eclipse.jst.jsp.core.text.IJSPPartitions;
 import org.eclipse.jst.jsp.ui.StructuredTextViewerConfigurationJSP;
 import org.eclipse.wst.html.core.text.IHTMLPartitions;
 import org.eclipse.wst.jsdt.web.ui.StructuredTextViewerConfigurationJSDT;
+import org.eclipse.wst.jsdt.web.ui.internal.autoedit.AutoEditStrategyForJs;
 import org.eclipse.wst.sse.ui.StructuredTextViewerConfiguration;
 import org.eclipse.wst.sse.ui.internal.provisional.style.LineStyleProvider;
 /**
@@ -58,4 +60,17 @@
 
 		return providers;
 	}
+	
+	/**
+	 * @see org.eclipse.jst.jsp.ui.StructuredTextViewerConfigurationJSP#getAutoEditStrategies(org.eclipse.jface.text.source.ISourceViewer, java.lang.String)
+	 */
+	public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
+		if(contentType.equals(IHTMLPartitions.SCRIPT) || contentType.equals(IHTMLPartitions.SCRIPT_EVENTHANDLER)) {
+			IAutoEditStrategy[] strategies = new IAutoEditStrategy[1];
+			strategies[0] = new AutoEditStrategyForJs();
+			return strategies;
+		} else {
+			return super.getAutoEditStrategies(sourceViewer, contentType);
+		}
+	}
 }
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/plugin.xml b/bundles/org.eclipse.wst.jsdt.web.ui/plugin.xml
index 577e477..604fd98 100644
--- a/bundles/org.eclipse.wst.jsdt.web.ui/plugin.xml
+++ b/bundles/org.eclipse.wst.jsdt.web.ui/plugin.xml
@@ -720,4 +720,16 @@
     </contentType>
   </proposalComputer>
 </extension>
+<extension point="org.eclipse.wst.sse.ui.characterPairing">
+	<inserter class="org.eclipse.wst.jsdt.web.ui.internal.text.JsCharacterPairInserter" id="org.eclipse.wst.jsdt.web.ui.inserter">
+  <contentTypeIdentifier
+        id="org.eclipse.wst.html.core.htmlsource"
+        partitions="org.eclipse.wst.html.SCRIPT, org.eclipse.wst.html.SCRIPT.EVENTHANDLER">
+  </contentTypeIdentifier>
+  <contentTypeIdentifier
+        id="org.eclipse.jst.jsp.core.jspsource"
+        partitions="org.eclipse.wst.html.SCRIPT, org.eclipse.wst.html.SCRIPT.EVENTHANDLER">
+  </contentTypeIdentifier>
+	</inserter>
+</extension>
 </plugin>
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/StructuredTextViewerConfigurationJSDT.java b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/StructuredTextViewerConfigurationJSDT.java
index ce46e32..a1d8070 100644
--- a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/StructuredTextViewerConfigurationJSDT.java
+++ b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/StructuredTextViewerConfigurationJSDT.java
@@ -10,7 +10,11 @@
  *******************************************************************************/
 package org.eclipse.wst.jsdt.web.ui;
 
+import org.eclipse.jface.text.IAutoEditStrategy;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.wst.html.core.text.IHTMLPartitions;
 import org.eclipse.wst.html.ui.StructuredTextViewerConfigurationHTML;
+import org.eclipse.wst.jsdt.web.ui.internal.autoedit.AutoEditStrategyForJs;
 
 /**
 *
@@ -37,4 +41,17 @@
 		// Must have empty constructor to createExecutableExtension
 		super();
 	}
+	
+	/**
+	 * @see org.eclipse.wst.html.ui.StructuredTextViewerConfigurationHTML#getAutoEditStrategies(org.eclipse.jface.text.source.ISourceViewer, java.lang.String)
+	 */
+	public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) {
+		if(contentType.equals(IHTMLPartitions.SCRIPT) || contentType.equals(IHTMLPartitions.SCRIPT_EVENTHANDLER)) {
+			IAutoEditStrategy[] strategies = new IAutoEditStrategy[1];
+			strategies[0] = new AutoEditStrategyForJs();
+			return strategies;
+		} else {
+			return super.getAutoEditStrategies(sourceViewer, contentType);
+		}
+	}
 }
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/autoedit/AutoEditStrategyForJs.java b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/autoedit/AutoEditStrategyForJs.java
index b662203..9c1f97c 100644
--- a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/autoedit/AutoEditStrategyForJs.java
+++ b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/autoedit/AutoEditStrategyForJs.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2008 IBM Corporation and others.
+ * Copyright (c) 2007, 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
@@ -53,8 +53,8 @@
 			return fStrategies;
 		}
 		String partitioning = IHTMLPartitions.SCRIPT;
-		fStrategies = new IAutoEditStrategy[] { new JavaDocAutoIndentStrategy(partitioning), new SmartSemicolonAutoEditStrategy(partitioning),
-				new JavaAutoIndentStrategy(partitioning, getJavaProject(document), null) };
+		fStrategies = new IAutoEditStrategy[] { new SmartSemicolonAutoEditStrategy(partitioning),
+				new JavaAutoIndentStrategy(partitioning, getJavaProject(document), null), new JavaDocAutoIndentStrategy(partitioning) };
 		/* new AutoEditStrategyForTabs() */
 		return fStrategies;
 	}
diff --git a/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/text/JsCharacterPairInserter.java b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/text/JsCharacterPairInserter.java
new file mode 100644
index 0000000..38d02d7
--- /dev/null
+++ b/bundles/org.eclipse.wst.jsdt.web.ui/src/org/eclipse/wst/jsdt/web/ui/internal/text/JsCharacterPairInserter.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (c) 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.jsdt.web.ui.internal.text;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.link.ILinkedModeListener;
+import org.eclipse.jface.text.link.LinkedModeModel;
+import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags;
+import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.wst.jsdt.core.JavaScriptCore;
+import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
+import org.eclipse.wst.jsdt.internal.ui.text.JavaHeuristicScanner;
+import org.eclipse.wst.jsdt.internal.ui.text.Symbols;
+import org.eclipse.wst.jsdt.ui.PreferenceConstants;
+import org.eclipse.wst.jsdt.web.ui.internal.Logger;
+import org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter;
+
+/**
+ * <p>Inserts character pairs in script regions in HTML and JSP documents based on the Javascript
+ * character pairing preferences.</p>
+ */
+public class JsCharacterPairInserter extends AbstractCharacterPairInserter implements IPropertyChangeListener{
+
+	private boolean fCloseStrings;
+	private boolean fCloseBrackets;
+	private boolean fCloseBraces;
+	private boolean fCloseAngularBrackets;
+	
+	/**
+	 * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#hasPair(char)
+	 */
+	public boolean hasPair(char c) {
+		switch (c) {
+			case '(':
+			case '<':
+			case '[':
+			case '\'':
+			case '\"':
+			case '{':
+				return true;
+			default:
+				return false;
+		}
+	}
+	
+	/**
+	 * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#shouldPair(org.eclipse.jface.text.source.ISourceViewer, char)
+	 */
+	protected boolean shouldPair(ISourceViewer viewer, char c) {
+		IDocument document= viewer.getDocument();
+
+		final Point selection= viewer.getSelectedRange();
+		final int offset= selection.x;
+		final int length= selection.y;
+
+		try {
+			IRegion startLine= document.getLineInformationOfOffset(offset);
+			IRegion endLine= document.getLineInformationOfOffset(offset + length);
+
+			JavaHeuristicScanner scanner= new JavaHeuristicScanner(document);
+			int nextToken= scanner.nextToken(offset + length, endLine.getOffset() + endLine.getLength());
+			String next= nextToken == Symbols.TokenEOF ? null : document.get(offset, scanner.getPosition() - offset).trim();
+			int prevToken= scanner.previousToken(offset - 1, startLine.getOffset());
+			int prevTokenOffset= scanner.getPosition() + 1;
+			String previous= prevToken == Symbols.TokenEOF ? null : document.get(prevTokenOffset, offset - prevTokenOffset).trim();
+
+			switch (c) {
+				case '(':
+					if (!fCloseBrackets
+							|| nextToken == Symbols.TokenLPAREN
+							|| nextToken == Symbols.TokenIDENT
+							|| next != null && next.length() > 1)
+						return false;
+					break;
+
+				case '<':
+					if (!(fCloseAngularBrackets && fCloseBrackets)
+							|| nextToken == Symbols.TokenLESSTHAN
+							|| 		   prevToken != Symbols.TokenLBRACE
+									&& prevToken != Symbols.TokenRBRACE
+									&& prevToken != Symbols.TokenSEMICOLON
+									&& prevToken != Symbols.TokenSYNCHRONIZED
+									&& prevToken != Symbols.TokenSTATIC
+									&& (prevToken != Symbols.TokenIDENT || !isAngularIntroducer(previous))
+									&& prevToken != Symbols.TokenEOF)
+						return false;
+					break;
+
+				case '{':
+					if (!fCloseBraces
+							|| nextToken == Symbols.TokenIDENT
+							|| next != null && next.length() > 1)
+						return false;
+					break;
+				case '[':
+					if (!fCloseBrackets
+							|| nextToken == Symbols.TokenIDENT
+							|| next != null && next.length() > 1)
+						return false;
+					break;
+
+				case '\'':
+				case '"':
+					if (!fCloseStrings
+							|| nextToken == Symbols.TokenIDENT
+							|| prevToken == Symbols.TokenIDENT
+							|| next != null && next.length() > 1
+							|| previous != null && previous.length() > 1)
+						return false;
+					break;
+
+				default:
+					return false;
+			}
+		} catch (BadLocationException e) {
+			return false;
+		}
+		
+		return true;
+	}
+	
+	/**
+	 * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#getPair(char)
+	 */
+	protected char getPair(char c) {
+		switch (c) {
+			case '(':
+				return ')';
+			case '<':
+				return '>';
+			case '[':
+				return ']';
+			case '{':
+				return '}';
+			case '\'':
+				return c;
+			case '\"':
+				return c;
+			default:
+				throw new IllegalArgumentException();
+		}
+	}
+	
+	/**
+	 * <p>Initialize the preference listener</p>
+	 * 
+	 * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#initialize()
+	 */
+	public void initialize() {
+		super.initialize();
+		IPreferenceStore preferenceStore = JavaScriptPlugin.getDefault().getPreferenceStore();
+		this.fCloseStrings = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_STRINGS);
+		this.fCloseBrackets = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACKETS);
+		this.fCloseBraces = preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACES);
+		this.fCloseAngularBrackets = JavaScriptCore.VERSION_1_5.compareTo(
+				preferenceStore.getString(JavaScriptCore.COMPILER_SOURCE)) <= 0;
+		preferenceStore.addPropertyChangeListener(this);
+	}
+	
+	/**
+	 * <p>Dispose the preference listener</p>
+	 * 
+	 * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#dispose()
+	 */
+	public void dispose() {
+		JavaScriptPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
+		super.dispose();
+	}
+
+	/**
+	 * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+	 */
+	public void propertyChange(PropertyChangeEvent event) {
+		if (PreferenceConstants.EDITOR_CLOSE_STRINGS.equals(event.getProperty())){
+			this.fCloseStrings = ((Boolean) event.getNewValue()).booleanValue();
+		} else if (PreferenceConstants.EDITOR_CLOSE_BRACKETS.equals(event.getProperty())) {
+			this.fCloseBrackets = ((Boolean) event.getNewValue()).booleanValue();
+		} else if (PreferenceConstants.EDITOR_CLOSE_BRACES.equals(event.getProperty())) {
+			this.fCloseBraces = ((Boolean) event.getNewValue()).booleanValue();
+		} else if (JavaScriptCore.COMPILER_SOURCE.equals(event.getProperty())) {
+			IPreferenceStore preferenceStore = JavaScriptPlugin.getDefault().getPreferenceStore();
+			this.fCloseAngularBrackets = JavaScriptCore.VERSION_1_5.compareTo(preferenceStore.getString(
+						JavaScriptCore.COMPILER_SOURCE)) <= 0;
+		}
+	}
+	
+	/**
+	 * TODO: IAN: comment me
+	 * @param identifier
+	 * @return
+	 */
+	private boolean isAngularIntroducer(String identifier) {
+		return identifier.length() > 0
+				&& (Character.isUpperCase(identifier.charAt(0))
+						|| identifier.startsWith("final") //$NON-NLS-1$
+						|| identifier.startsWith("public") //$NON-NLS-1$
+						|| identifier.startsWith("public") //$NON-NLS-1$
+						|| identifier.startsWith("protected") //$NON-NLS-1$
+						|| identifier.startsWith("private")); //$NON-NLS-1$
+	}
+	
+	/**
+	 * @see org.eclipse.wst.sse.ui.typing.AbstractCharacterPairInserter#getExitPolicy(char, char, org.eclipse.jface.text.IDocument)
+	 */
+	protected IExitPolicy getExitPolicy(char exit, char escape, IDocument document) {
+		return new ExitPolicy(exit, escape, document);
+	}
+	
+	/**
+	 * <p>An exit policy that deals with a user adding a carriage return between {}.
+	 * In that case rather then exiting to the exit position the linked mode is exited
+	 * and the carriage return inserted.</p>
+	 */
+	private static class ExitPolicy implements IExitPolicy {
+
+		/** exit character */
+		private char fExit;
+		
+		/** escape character for the exit character */
+		private char fEscape;
+		
+		/** document to execute this exit policy on */
+		private IDocument fDocument;
+
+		/**
+		 * <p>Default constructor</p>
+		 * 
+		 * @param exit exit character
+		 * @param escape escape character for the exit character
+		 * @param document document to execute this exit policy on
+		 */
+		public ExitPolicy(char exit, char escape, IDocument document) {
+			fExit = exit;
+			fEscape = escape;
+			fDocument = document;
+		}
+
+		/**
+		 * @see org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy#doExit(org.eclipse.jface.text.link.LinkedModeModel, org.eclipse.swt.events.VerifyEvent, int, int)
+		 */
+		public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) {
+			if(!isMasked(offset)) {
+				// if exit character then exit to exit location
+				if (event.character == fExit) {
+					return new ExitFlags(ILinkedModeListener.UPDATE_CARET, false);
+				}
+				
+				// if carriage return and previous character is { then exit linked mode and insert return
+				if (event.character == SWT.CR && offset > 0) {
+					try {
+						if (this.fDocument.getChar(offset - 1) == '{') {
+							return new ExitFlags(ILinkedModeListener.EXIT_ALL, true);
+						}
+					} catch (BadLocationException e) {
+						Logger.logException("Error while trying to exit linked mode", e); //$NON-NLS-1$
+					}
+				}
+			}
+			return null;
+		}
+
+		/**
+		 * <p>Determine if the exit character has been escaped.<p>
+		 * 
+		 * @param offset current offset in the document
+		 * @return <code>true</code> if exit character escaped, <code>false</code> otherwise
+		 */
+		private boolean isMasked(int offset) {
+			try {
+				return fEscape == fDocument.getChar(offset - 1);
+			} catch (BadLocationException e) {
+			}
+			return false;
+		}
+	}
+}