From e81c08a0e59ca182e67cff4723c6e27ab983487c Mon Sep 17 00:00:00 2001
From: vrubezhny <vrubezhny@exadel.com>
Date: Thu, 7 Feb 2013 04:26:12 +0400
Subject: [PATCH] (JBIDE-13400) PageContextFactory.ExtendedLinkElementAdapter
 is leaked The issue is fixed by avoiding the adding of
 ExtendedLinkElementAdapter to the nodes.
 ExtendedLinkElementAdapter is used only on a temporary
 basis and as such no leaks should occur.

	modified:   plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSJSPRecognizer.java
	modified:   plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSSelectorTreeModel.java
	modified:   plugins/org.jboss.tools.jst.text.ext/src/org/jboss/tools/jst/text/ext/hyperlink/CSSClassHyperlink.java
	modified:   plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/PageContextFactory.java
	modified:   plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/internal/proposal/CSSClassProposalType.java
---
 .../dialog/selector/model/CSSJSPRecognizer.java    |   11 +-
 .../selector/model/CSSSelectorTreeModel.java       |   44 ++---
 .../jst/text/ext/hyperlink/CSSClassHyperlink.java  |   31 ++--
 .../jboss/tools/jst/web/kb/PageContextFactory.java |  184 +++++++++++---------
 .../kb/internal/proposal/CSSClassProposalType.java |   14 +-
 5 files changed, 157 insertions(+), 127 deletions(-)

diff --git a/plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSJSPRecognizer.java b/plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSJSPRecognizer.java
index 1a8ed23..7d672b8 100644
--- a/plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSJSPRecognizer.java
+++ b/plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSJSPRecognizer.java
@@ -1,12 +1,12 @@
 /*******************************************************************************
- * Copyright (c) 2007-2010 Exadel, Inc. and Red Hat, Inc.
+ * Copyright (c) 2007-2013 Red Hat, Inc.
  * Distributed under license by Red Hat, Inc. All rights reserved.
  * This program is 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:
- *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
+ *     Red Hat, Inc. - initial API and implementation
  ******************************************************************************/
 package org.jboss.tools.jst.css.dialog.selector.model;
 
@@ -68,8 +68,11 @@ public class CSSJSPRecognizer {
 		java.util.List<CSSRuleList> cssRuleLists = new ArrayList<CSSRuleList>(0);
 		styleSheets = new ArrayList<CSSStyleSheet>(0);
 		for (int i = 0; i < descrs.size(); i++) {
-			styleSheets.add(descrs.get(i).sheet);
-			cssRuleLists.add(descrs.get(i).sheet.getCssRules());
+			CSSStyleSheet sheet = descrs.get(i).getStylesheet();
+			if (sheet != null) {
+				styleSheets.add(sheet);
+				cssRuleLists.add(sheet.getCssRules());
+			}
 		}
 		if (cssRuleLists.size() == 0) {
 			return null;
diff --git a/plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSSelectorTreeModel.java b/plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSSelectorTreeModel.java
index 52763e1..f6c3dbc 100644
--- a/plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSSelectorTreeModel.java
+++ b/plugins/org.jboss.tools.jst.css/src/org/jboss/tools/jst/css/dialog/selector/model/CSSSelectorTreeModel.java
@@ -1,12 +1,12 @@
 /*******************************************************************************
- * Copyright (c) 2007-2010 Exadel, Inc. and Red Hat, Inc.
+ * Copyright (c) 2007-2013 Red Hat, Inc.
  * Distributed under license by Red Hat, Inc. All rights reserved.
  * This program is 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:
- *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
+ *     Red Hat, Inc. - initial API and implementation
  ******************************************************************************/
 
 package org.jboss.tools.jst.css.dialog.selector.model;
@@ -39,25 +39,27 @@ public class CSSSelectorTreeModel {
 
 	private void initModel(CSSStyleSheetDescriptor[] cssStyleSheets) {
 		for (int i = 0; i < cssStyleSheets.length; i++) {
-			CSSStyleSheet styleSheet = cssStyleSheets[i].sheet;
-			CSSTreeNode parentSheet = new CSSTreeNode(cssStyleSheets[i].source);
-			parentSheet.setStyleSheetSource(parentSheet.toString());
-			invisibleRoot.addChild(parentSheet);
-			parentSheet.setCSSContainer(new CSSStyleSheetContainer(styleSheet,
-					cssStyleSheets[i].source));
-			CSSRuleList cssRuleList = styleSheet.getCssRules();
-			for (int j = 0; j < cssRuleList.getLength(); j++) {
-				CSSRule cssRule = cssRuleList.item(j);
-				if (cssRule.getType() == CSSRule.STYLE_RULE) {
-					String[] selectors = CSSSelectorUtils
-						.parseSelectorName(((ICSSStyleRule) cssRule)
-								.getSelectorText());
-					for (int k = 0; k < selectors.length; k++) {
-						CSSTreeNode ruleNode = new CSSTreeNode(selectors[k]);
-						ruleNode.setCSSContainer(new CSSRuleContainer(selectors[k],
-							cssRule, cssStyleSheets[i].source));
-						ruleNode.setStyleSheetSource(cssStyleSheets[i].source);
-						parentSheet.addChild(ruleNode);
+			CSSStyleSheet styleSheet = cssStyleSheets[i].getStylesheet();
+			if (styleSheet != null) {
+				CSSTreeNode parentSheet = new CSSTreeNode(cssStyleSheets[i].getSource());
+				parentSheet.setStyleSheetSource(parentSheet.toString());
+				invisibleRoot.addChild(parentSheet);
+				parentSheet.setCSSContainer(new CSSStyleSheetContainer(styleSheet,
+						cssStyleSheets[i].getSource()));
+				CSSRuleList cssRuleList = styleSheet.getCssRules();
+				for (int j = 0; j < cssRuleList.getLength(); j++) {
+					CSSRule cssRule = cssRuleList.item(j);
+					if (cssRule.getType() == CSSRule.STYLE_RULE) {
+						String[] selectors = CSSSelectorUtils
+							.parseSelectorName(((ICSSStyleRule) cssRule)
+									.getSelectorText());
+						for (int k = 0; k < selectors.length; k++) {
+							CSSTreeNode ruleNode = new CSSTreeNode(selectors[k]);
+							ruleNode.setCSSContainer(new CSSRuleContainer(selectors[k],
+								cssRule, cssStyleSheets[i].getSource()));
+							ruleNode.setStyleSheetSource(cssStyleSheets[i].getSource());
+							parentSheet.addChild(ruleNode);
+						}
 					}
 				}
 			}
diff --git a/plugins/org.jboss.tools.jst.text.ext/src/org/jboss/tools/jst/text/ext/hyperlink/CSSClassHyperlink.java b/plugins/org.jboss.tools.jst.text.ext/src/org/jboss/tools/jst/text/ext/hyperlink/CSSClassHyperlink.java
index 6b1ab27..e534801 100644
--- a/plugins/org.jboss.tools.jst.text.ext/src/org/jboss/tools/jst/text/ext/hyperlink/CSSClassHyperlink.java
+++ b/plugins/org.jboss.tools.jst.text.ext/src/org/jboss/tools/jst/text/ext/hyperlink/CSSClassHyperlink.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011-2012 Red Hat, Inc.
+ * Copyright (c) 2011-2013 Red Hat, Inc.
  * Distributed under license by Red Hat, Inc. All rights reserved.
  * This program is made available under the terms of the
  * Eclipse Public License v1.0 which accompanies this distribution,
@@ -53,6 +53,7 @@ import org.w3c.dom.css.CSSMediaRule;
 import org.w3c.dom.css.CSSRule;
 import org.w3c.dom.css.CSSRuleList;
 import org.w3c.dom.css.CSSStyleRule;
+import org.w3c.dom.css.CSSStyleSheet;
 
 /**
  * @author Jeremy
@@ -82,12 +83,15 @@ public class CSSClassHyperlink extends AbstractHyperlink {
 		
 		for (int i = (descrs == null) ? -1 : descrs.size() - 1; descrs != null && i >= 0; i--) {
 			CSSStyleSheetDescriptor descr = descrs.get(i);
-			CSSRuleList rules = descr.sheet.getCssRules();
-			for (int r = 0; rules != null && r < rules.getLength(); r++) {
-				Set<CSSRuleDescriptor> matches = getMatchedRuleDescriptors(rules.item(r), getStyleName(region));
-				for (CSSRuleDescriptor match : matches) {
-					match.stylesheetDescriptor = descr;
-					bestMatchDescriptors.add(match);
+			CSSStyleSheet sheet = descr.getStylesheet();
+			if (sheet != null) {
+				CSSRuleList rules = sheet.getCssRules();
+				for (int r = 0; rules != null && r < rules.getLength(); r++) {
+					Set<CSSRuleDescriptor> matches = getMatchedRuleDescriptors(rules.item(r), getStyleName(region));
+					for (CSSRuleDescriptor match : matches) {
+						match.stylesheetDescriptor = descr;
+						bestMatchDescriptors.add(match);
+					}
 				}
 			}
 		}
@@ -100,16 +104,17 @@ public class CSSClassHyperlink extends AbstractHyperlink {
 			IFile file = findFileForCSSStyleSheet(descr.getFilePath());
 			if (file != null) {
 				int startOffset = 0;
-				if (descr.sheet.getOwnerNode() != null) {
-					Node node = descr.sheet.getOwnerNode().getFirstChild();
+				CSSStyleSheet sheet = descr.getStylesheet();
+				if (sheet != null && descr.getContainerNode() != null) {
+					Node node = descr.getContainerNode().getFirstChild();
 					if (node instanceof IndexedRegion) {
 						startOffset = ((IndexedRegion)node).getStartOffset();
 					}
+					showRegion(
+							file, 
+							new Region(startOffset + ((IndexedRegion)d.rule).getStartOffset(), ((IndexedRegion)d.rule).getLength()));
+					return;
 				}
-				showRegion(
-						file, 
-						new Region(startOffset + ((IndexedRegion)d.rule).getStartOffset(), ((IndexedRegion)d.rule).getLength()));
-				return;
 			}
 		}
 
diff --git a/plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/PageContextFactory.java b/plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/PageContextFactory.java
index ddd006b..9debeb9 100644
--- a/plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/PageContextFactory.java
+++ b/plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/PageContextFactory.java
@@ -1,5 +1,5 @@
 /******************************************************************************* 
- * Copyright (c) 2011-2012 Red Hat, Inc. 
+ * Copyright (c) 2011-2013 Red Hat, Inc. 
  * Distributed under license by Red Hat, Inc. All rights reserved. 
  * This program is made available under the terms of the 
  * Eclipse Public License v1.0 which accompanies this distribution, 
@@ -983,54 +983,45 @@ public class PageContextFactory implements IResourceChangeListener {
 
 	private static void fillCSSStyleSheetFromAttribute(IDOMElement node,
 			String attribute, ICSSContainerSupport context, boolean jsf2Source) {
-		CSSStyleSheetDescriptor descr = getSheetForTagAttribute(node, attribute, jsf2Source);
-		if (descr != null) {
-			context.addCSSStyleSheetDescriptor(descr);
-		}
+		context.addCSSStyleSheetDescriptor(new CSSStyleSheetDescriptorForAttribute(node, attribute, jsf2Source));
 	}
 
 	private static void fillCSSStyleSheetFromElement(IDOMElement node,
 			ICSSContainerSupport context, boolean jsf2Source) {
-		CSSStyleSheet sheet = getSheetForTag(node);
-		if (sheet != null) {
-			String library = null;
-			if (jsf2Source) {
-				Attr libraryAttr = node.getAttributeNode("library"); //$NON-NLS-1$
-				if (libraryAttr != null && libraryAttr.getNodeValue() != null) {
-					library = libraryAttr.getNodeValue().trim();
-					library = library.length() == 0 ? null : library;
-				}
-			}
-
-			context.addCSSStyleSheetDescriptor(new CSSStyleSheetDescriptor(context.getResource().getFullPath().toString(), sheet, jsf2Source, library));
-		}
+		context.addCSSStyleSheetDescriptor(new CSSStyleSheetDescriptor(context.getResource().getFullPath().toString(), node, jsf2Source));
 	}
 
 	private static final String JSF2_RESOURCES_FOLDER = "/resources"; //$NON-NLS-1$
 
 	public static class CSSStyleSheetDescriptor {
-		public CSSStyleSheet sheet;
-		public String source;
-		public boolean jsf2Source;
-		public String jsf2Library;
+		protected Node stylesheetContainer;
+		protected String source;
+		protected boolean jsf2Source;
+		protected boolean initialized = false;
 
-		/*
-		CSSStyleSheetDescriptor (String source, CSSStyleSheet sheet) {
-			this(source, sheet, false);
-		}
-		*/
-		
-		CSSStyleSheetDescriptor (String source, CSSStyleSheet sheet, boolean jsf2Source, String jsf2Library) {
+		CSSStyleSheetDescriptor (String source, Node styleheetContainer, boolean jsf2Source) {
 			this.source = source;
-			this.sheet = sheet;
+			this.stylesheetContainer = styleheetContainer;
 			this.jsf2Source = jsf2Source;
-			this.jsf2Library  = jsf2Library;
 		}
-		
+
 		public String getFilePath() {
+			if (!initialized) {
+				getStylesheet();
+			}
+			
 			if (!jsf2Source)
 				return source;
 			
+			String jsf2Library = null;
+			if (jsf2Source && stylesheetContainer instanceof Element) {
+				Attr libraryAttr = ((Element)stylesheetContainer).getAttributeNode("library"); //$NON-NLS-1$
+				if (libraryAttr != null && libraryAttr.getNodeValue() != null) {
+					jsf2Library = libraryAttr.getNodeValue().trim();
+					jsf2Library = jsf2Library.length() == 0 ? null : jsf2Library;
+				}
+			}		
+			
 			if (jsf2Library != null) {
 				String library = jsf2Library.trim();
 				if (library.length() != 0) {
@@ -1039,71 +1030,96 @@ public class PageContextFactory implements IResourceChangeListener {
 			}
 			return JSF2_RESOURCES_FOLDER + '/' + source;
 		}
-	}
-
-	/**
-	 * 
-	 * @param stylesContainer
-	 * @return
-	 */
-	private static CSSStyleSheetDescriptor getSheetForTagAttribute(final Node stylesContainer, String attribute, boolean jsf2Source) {
-		INodeNotifier notifier = (INodeNotifier) stylesContainer;
-		CSSStyleSheet sheet = null;
-		String source = null;
-
-		synchronized (notifier) {
-			IStyleSheetAdapter adapter = (IStyleSheetAdapter) notifier.getAdapterFor(IStyleSheetAdapter.class);
-
-			if (adapter != null && !(adapter instanceof ExtendedLinkElementAdapter)) {
-				notifier.removeAdapter(adapter);
-				adapter = new ExtendedLinkElementAdapter(
-						(Element) stylesContainer, attribute, jsf2Source);
-				sheet = (CSSStyleSheet) adapter.getSheet();
-				source = ((ExtendedLinkElementAdapter)adapter).getSource();
-				if (sheet != null && source != null) {
-					notifier.addAdapter(adapter);
-				}
-			}
 		
-			if (adapter != null) {
-				sheet = (CSSStyleSheet) adapter.getSheet();
-				source = ((ExtendedLinkElementAdapter)adapter).getSource();
+		public Node getContainerNode() {
+			return stylesheetContainer;
+		}
+		
+		public String getSource() {
+			if (!initialized) {
+				getStylesheet();
 			}
-			
+			return source;
 		}
 
-		String library = null;
-		if (jsf2Source && stylesContainer instanceof Element) {
-			Attr libraryAttr = ((Element)stylesContainer).getAttributeNode("library"); //$NON-NLS-1$
-			if (libraryAttr != null && libraryAttr.getNodeValue() != null) {
-				library = libraryAttr.getNodeValue().trim();
-				library = library.length() == 0 ? null : library;
+		/**
+		 * 
+		 * @param stylesContainer
+		 * @return
+		 */
+		public CSSStyleSheet getStylesheet() {
+			if (stylesheetContainer == null)
+				return null;
+			
+			INodeNotifier notifier = (INodeNotifier) stylesheetContainer;
+			CSSStyleSheet sheet = null;
+
+			synchronized (notifier) {
+				IStyleSheetAdapter adapter = (IStyleSheetAdapter) notifier.getAdapterFor(IStyleSheetAdapter.class);
+
+				if (adapter != null) {
+					sheet = (CSSStyleSheet) adapter.getSheet();
+				}
 			}
-		}
 
-		return sheet == null || source == null ? null : new CSSStyleSheetDescriptor(source, sheet, jsf2Source, library);
+			initialized = true;
+			return sheet;
+		}
 	}
 
-	/**
-	 * 
-	 * @param stylesContainer
-	 * @return
-	 */
-	private static CSSStyleSheet getSheetForTag(final Node stylesContainer) {
-		INodeNotifier notifier = (INodeNotifier) stylesContainer;
-		CSSStyleSheet sheet = null;
+	public static class CSSStyleSheetDescriptorForAttribute extends CSSStyleSheetDescriptor {
+		public String attribute;
+		
+		public CSSStyleSheetDescriptorForAttribute(Node styleheetContainer, String attribute, boolean jsf2Source) {
+			super(null, styleheetContainer, jsf2Source);
+			this.attribute = attribute;
+		}
+		
+		/**
+		 * 
+		 * @param stylesContainer
+		 * @return
+		 */
+		public CSSStyleSheet getStylesheet() {
+			if (stylesheetContainer == null)
+				return null;
+			
+			INodeNotifier notifier = (INodeNotifier) stylesheetContainer;
+			CSSStyleSheet sheet = null;
+
+			synchronized (notifier) {
+				IStyleSheetAdapter originalAdapter = (IStyleSheetAdapter) notifier.getAdapterFor(IStyleSheetAdapter.class);
+				if (originalAdapter != null)
+					notifier.removeAdapter(originalAdapter);
+				
+				IStyleSheetAdapter tempAdapter = new ExtendedLinkElementAdapter(
+						(Element) stylesheetContainer, attribute, jsf2Source);
+				
+				// These getSheet()/getSource() calls are here just to ensure that the CSS Stylesheet exists
+				// But we can't use the sheet returned by getSheet() call because it's improperly initialized
+				// unless the adapter is registered on the node 
+				sheet = (CSSStyleSheet) tempAdapter.getSheet();
+				this.source = ((ExtendedLinkElementAdapter)tempAdapter).getSource();
+				if (sheet != null && source != null) {
+					// So, do register adapter
+					notifier.addAdapter(tempAdapter);
+				}
 
-		synchronized (notifier) {
-			IStyleSheetAdapter adapter = (IStyleSheetAdapter) notifier.getAdapterFor(IStyleSheetAdapter.class);
+				// Re-get the values
+				sheet = (CSSStyleSheet) tempAdapter.getSheet();
+				this.source = ((ExtendedLinkElementAdapter)tempAdapter).getSource();
 
-			if (adapter != null) {
-				sheet = (CSSStyleSheet) adapter.getSheet();
+				// And restore the original adapter (if so)
+				notifier.removeAdapter(tempAdapter);
+				if (originalAdapter != null)
+					notifier.addAdapter(originalAdapter);
 			}
-		}
 
-		return sheet;
+			initialized = true;
+			return (CSSStyleSheet) sheet;
+		}
 	}
-
+	
 	private static boolean containsPrefix(Map<String, List<INameSpace>> ns, String prefix) {
 		for (List<INameSpace> n: ns.values()) {
 			for (INameSpace nameSpace : n) {
diff --git a/plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/internal/proposal/CSSClassProposalType.java b/plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/internal/proposal/CSSClassProposalType.java
index c631851..8758f99 100644
--- a/plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/internal/proposal/CSSClassProposalType.java
+++ b/plugins/org.jboss.tools.jst.web.kb/src/org/jboss/tools/jst/web/kb/internal/proposal/CSSClassProposalType.java
@@ -1,5 +1,5 @@
 /******************************************************************************* 
- * Copyright (c) 2009-2011 Red Hat, Inc. 
+ * Copyright (c) 2009-2013 Red Hat, Inc. 
  * Distributed under license by Red Hat, Inc. All rights reserved. 
  * This program is made available under the terms of the 
  * Eclipse Public License v1.0 which accompanies this distribution, 
@@ -26,6 +26,7 @@ import org.w3c.dom.css.CSSMediaRule;
 import org.w3c.dom.css.CSSRule;
 import org.w3c.dom.css.CSSRuleList;
 import org.w3c.dom.css.CSSStyleRule;
+import org.w3c.dom.css.CSSStyleSheet;
 
 /**
  * The CSS Class proposal type. Is used to collect and return the proposals on
@@ -51,10 +52,13 @@ public class CSSClassProposalType extends CustomProposalType {
 			List<CSSStyleSheetDescriptor> descrs = cssSource.getCSSStyleSheetDescriptors();
 			if (descrs != null) {
 				for (CSSStyleSheetDescriptor descr : descrs) {
-					CSSRuleList rules = descr.sheet.getCssRules();
-					for (int i = 0; rules != null && i < rules.getLength(); i++) {
-						CSSRule rule = rules.item(i);
-						idList.addAll(getClassNamesFromCSSRule(rule));
+					CSSStyleSheet sheet = descr.getStylesheet();
+					if (sheet != null) {
+						CSSRuleList rules = sheet.getCssRules();
+						for (int i = 0; rules != null && i < rules.getLength(); i++) {
+							CSSRule rule = rules.item(i);
+							idList.addAll(getClassNamesFromCSSRule(rule));
+						}
 					}
 				}
 			}
-- 
1.7.10.4

