blob: 2eeaeeec7b24c4477fd20f5a57989fd9ae3519d1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Sybase, Inc. 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:
* Sybase, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.pagedesigner.editors.palette.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.gef.palette.PaletteDrawer;
import org.eclipse.gef.palette.PaletteEntry;
import org.eclipse.jst.jsf.common.internal.JSPUtil;
import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.Namespace;
import org.eclipse.jst.jsf.core.internal.CompositeTagRegistryFactory;
import org.eclipse.jst.jsf.core.internal.CompositeTagRegistryFactory.TagRegistryIdentifier;
import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry;
import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry.ITagRegistryListener;
import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry.TagRegistryChangeEvent;
import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry.TagRegistryChangeEvent.EventType;
import org.eclipse.jst.pagedesigner.PDPlugin;
import org.eclipse.jst.pagedesigner.editors.palette.DesignerPaletteCustomizationsHelper;
import org.eclipse.jst.pagedesigner.editors.palette.IEntryChangeListener;
import org.eclipse.jst.pagedesigner.editors.palette.IPaletteConstants;
import org.eclipse.jst.pagedesigner.editors.palette.IPaletteContext;
import org.eclipse.jst.pagedesigner.editors.palette.IPaletteItemManager;
import org.eclipse.wst.html.core.internal.contentmodel.HTMLCMDocumentFactory;
import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument;
import org.eclipse.wst.xml.core.internal.provisional.contentmodel.CMDocType;
/**
* Manages tag library palette by palette context. Capable of handling JSP and XHTML content types.
*
* Callers must use getInstance(IPaletteContext), and when done, call release(IFile).
*
* @author mengbo and others
*/
public class PaletteItemManager implements IPaletteItemManager,
IPaletteConstants, ITagRegistryListener {
private static final boolean DEBUG = false;
private static Map<TagRegistryIdentifier, PaletteItemManager> _managers = new HashMap<TagRegistryIdentifier, PaletteItemManager>();
private static ReentrantLock MANAGER_LOCK = new ReentrantLock();
private static long MANAGER_LOCK_TIMEOUT = 120;
private Set<IFile> _files = new HashSet<IFile>();
private TagRegistryIdentifier _tagRegId;
private List<PaletteDrawer> _paletteCategories = new ArrayList<PaletteDrawer>();
private CopyOnWriteArrayList<IEntryChangeListener> _listeners = new CopyOnWriteArrayList<IEntryChangeListener>();
private AtomicBoolean IS_DISPOSED = new AtomicBoolean();
private PaletteHelper _paletteHelper;
private ITagRegistry _tagRegistry;
/**
* Return singleton paletteItemManager for a given project. Will only work for JSPs.
* @param project
* @return PaletteItemManager
* @deprecated - use getInstance(paletteContext)
*/
public static PaletteItemManager getInstance(final IProject project) {
if (project == null) {
// sometimes when the editor is editing a file in jar file, may not
// be able to
// get the project.
return getInstance(createPaletteContext(null));
}
//relies on JSP file extension for content type
return getInstance(createPaletteContext(project.getFile("dummy.jsp"))); //$NON-NLS-1$
}
/**
* @param paletteContext
* @return PaletteItemManager instance shared with all files with same palette context in a project
* May return null if locking issue
*/
public static PaletteItemManager getInstance(final IPaletteContext paletteContext) {
boolean hasLock = false;
try {
if (MANAGER_LOCK.tryLock(MANAGER_LOCK_TIMEOUT, TimeUnit.SECONDS)){
hasLock = true;
final TagRegistryIdentifier regId = getTagRegistryIdentifier(paletteContext);
if (regId == null) {
PDPlugin.log(new Status(IStatus.ERROR, PDPlugin.getPluginId(), "Unable to display palette for "+paletteContext.getFile().getName()+". Unknown content type for file.")); //$NON-NLS-1$//$NON-NLS-2$
return null;
}
PaletteItemManager manager = _managers.get(regId);
if (manager == null) {
manager = new PaletteItemManager(regId);
_managers.put(regId, manager);
manager.init();
}
manager.addFile(paletteContext.getFile());
return manager;
}
//if we get here then the lock has timed out
PDPlugin.log(new Status(Status.ERROR, PDPlugin.getPluginId(), "(getInstance()) Failed to get managers lock for" + paletteContext.getFile().toString())); //$NON-NLS-1$
} catch (InterruptedException e) {
PDPlugin.log("Failed in PaletteItemManager.getInstance(PaletteContext", e); //$NON-NLS-1$
} finally {
if (hasLock)
MANAGER_LOCK.unlock();
}
return null;
}
private static TagRegistryIdentifier getTagRegistryIdentifier(
final IPaletteContext paletteContext) {
final IFile file = paletteContext.getFile();
if (file != null) {
final IContentTypeManager typeManager = Platform.getContentTypeManager();
final IContentType contentType =
typeManager.findContentTypeFor(file.getName());
if (contentType != null)
{
return new TagRegistryIdentifier(file.getProject(), contentType);
}
return null;
}
//to support legacy null projects. Allows HTML and JSP tag libs to be displayed.
return new TagRegistryIdentifier(null, org.eclipse.jst.pagedesigner.utils.JSPUtil.JSP_CONTENTTYPE);
}
/**
* @param file
* @return IPaletteContext
*/
public static IPaletteContext createPaletteContext(final IFile file) {
return new IPaletteContext() {
public IFile getFile() {
return file;
}
public Object getAdapter(Class adapter) {
return null;
}
};
}
private void addFile(final IFile file) {
synchronized (_files) {
_files.add(file);
}
}
/**
* Indicates that the file no longer needs the paletteItemManager, freeing the manager to be released after last reference
* @param paletteContext
*/
public void release(final IPaletteContext paletteContext) {
final IFile file = paletteContext.getFile();
boolean isEmpty = false;
synchronized (_files) {
if (_files.contains(file)) {
_files.remove(file);
if (_files.isEmpty())
isEmpty = true;
}
}
if (isEmpty && IS_DISPOSED.compareAndSet(false, true)) {
removeTagRegistryListeners(this);
boolean hasLock = false;
try {
if (MANAGER_LOCK.tryLock(MANAGER_LOCK_TIMEOUT, TimeUnit.SECONDS)) {
hasLock = true;
_managers.remove(_tagRegId);
}
else {
PDPlugin.log(new Status(Status.ERROR, PDPlugin.getPluginId(), "(Release) Failed to get managers lock for" + paletteContext.getFile().toString())); //$NON-NLS-1$
}
} catch (InterruptedException e) {
PDPlugin.log("Failed to release paletteItemManager for" + paletteContext.getFile().toString(), e); //$NON-NLS-1$
} finally {
if (hasLock)
MANAGER_LOCK.unlock();
}
}
}
private static void removeTagRegistryListeners(final PaletteItemManager manager) {
if (manager.getTagRegistry() != null)
manager.getTagRegistry().removeListener(manager);
}
private ITagRegistry getTagRegistry() {
return _tagRegistry;
}
/**
* For JUnit testing purposes only
*/
public static void clearPaletteItemManager() {
boolean hasLock = false;
try {
if (MANAGER_LOCK.tryLock(MANAGER_LOCK_TIMEOUT, TimeUnit.SECONDS)){
hasLock = true;
if (_managers == null)
return;
for (final PaletteItemManager manager : _managers.values()) {
PaletteItemManager.removeTagRegistryListeners(manager);
manager._files.clear();
}
_managers.clear();
} else {
//if we get here then the lock has timed out
PDPlugin.log(new Status(Status.ERROR, PDPlugin.getPluginId(), "(clear) Failed to get managers lock")); //$NON-NLS-1$
}
} catch (InterruptedException e) {
PDPlugin.log("Failed in clearPaletteItemManager", e); //$NON-NLS-1$
} finally {
if (hasLock)
MANAGER_LOCK.unlock();
}
}
private PaletteItemManager(final TagRegistryIdentifier regId) {
_paletteHelper = new PaletteHelper(this);
if (regId != null) {
_tagRegId = regId;
// init();
}
}
public List getAllCategories() {
synchronized (_paletteCategories) {
final List<PaletteDrawer> readOnlyCategories = new ArrayList<PaletteDrawer>(_paletteCategories);
return Collections.unmodifiableList(readOnlyCategories);
}
}
/**
* Initializes the palette items for the current project
*/
protected synchronized void init() {
synchronized (_paletteCategories) {
_paletteCategories.clear();
}
initTagRegistry();
DesignerPaletteCustomizationsHelper.loadUserCustomizations(this);
sortCategories();
}
/**
* Sort palette categories
*/
protected void sortCategories() {
//note that once we store ordering customizations, we will need to do something different
synchronized(_paletteCategories) {
Collections.sort(_paletteCategories, new Comparator(){
public int compare(Object o1, Object o2) {
String label1 = ((PaletteEntry)o1).getLabel();
String label2 = ((PaletteEntry)o2).getLabel();
return label1.compareTo(label2);
}
});
}
}
/**
* Reinitializes the palatteItemManager and informs all palette roots that use the manager to refresh
*/
public void reset() {
init();
fireModelChanged(null, null);
}
private void initTagRegistry() {
registerHTMLCategory();
if (isJSP(_tagRegId))
registerJSPCategory();
registerTagsFromTagRegistry();
}
private boolean isJSP(final TagRegistryIdentifier tagRegistryId) {
final IContentType ct = tagRegistryId.getContentType();
if (JSPUtil.isJSPContentType(ct.getId()))
return true;
return false;
}
private void registerTagsFromTagRegistry() {
_tagRegistry = getTagRegistry(_tagRegId);
if (_tagRegistry != null) {
for (final Namespace ns : _tagRegistry.getAllTagLibraries()) {
_paletteHelper.configPaletteItemsByNamespace(this, ns);
}
}
}
private ITagRegistry getTagRegistry(final TagRegistryIdentifier regId) {
ITagRegistry reg = null;
if (regId.getProject() != null) {
reg = CompositeTagRegistryFactory.getInstance().getRegistry(regId);
if (reg != null) {
reg.addListener(this);
}
}
return reg;
}
private void registerHTMLCategory() {
final CMDocument doc = HTMLCMDocumentFactory.getCMDocument(CMDocType.HTML_DOC_TYPE);
_paletteHelper.getOrCreateTaglibPaletteDrawer(this, doc, CMDocType.HTML_DOC_TYPE);
}
private void registerJSPCategory() {
final CMDocument doc = HTMLCMDocumentFactory.getCMDocument(CMDocType.JSP11_DOC_TYPE);
_paletteHelper.getOrCreateTaglibPaletteDrawer(this, doc, CMDocType.JSP11_DOC_TYPE);
}
// /**
// * Search Classpath entry list to find if the entry is jar library and the
// * library have the tld descriptor, if have ,build a palette category mapping
// * the tld descriptor.
// *
// * @param project
// */
// private void registerTldFromClasspath(final IProject project) {
// if (project != null) {
// ITaglibRecord[] tldrecs = TaglibIndex.getAvailableTaglibRecords(project.getFullPath());
// for (int i=0;i<tldrecs.length;i++){
// _paletteHelper.configPaletteItemsByTLD(this, getCurProject(), tldrecs[i]);
// }
// }
// }
/**
* @param id (most likely the uri)
* @param label
* @return TaglibPaletteDrawer
*/
public TaglibPaletteDrawer findOrCreateCategory(final String id, final String label) {
TaglibPaletteDrawer category = getTaglibPalletteDrawer(id);
if (category == null)
category = createTaglibPaletteDrawer(id, label);
return category;
}
/**
* @param uri
* @return TaglibPaletteDrawer
*/
public TaglibPaletteDrawer findCategoryByURI(final String uri) {
TaglibPaletteDrawer category;
for (final Iterator iter = getAllCategories().iterator(); iter.hasNext();) {
category = (TaglibPaletteDrawer) iter.next();
if (uri.equals(category.getURI())) {
return category;
}
}
return null;
}
public TaglibPaletteDrawer createTaglibPaletteDrawer(final String uri, final String label) {
final TaglibPaletteDrawer r = new TaglibPaletteDrawer(uri, label);
synchronized(_paletteCategories) {
_paletteCategories.add(r);
}
return r;
}
public TaglibPaletteDrawer getTaglibPalletteDrawer(final String uri) {
for (final Iterator iter = getAllCategories().iterator(); iter.hasNext();) {
final TaglibPaletteDrawer cat = (TaglibPaletteDrawer) iter.next();
if (uri.equalsIgnoreCase(cat.getId())) {
return cat;
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see com.sybase.stf.jmt.pagedesigner.editors.palette.IPaletteItemManager#addEntryChangeListener(com.sybase.stf.jmt.pagedesigner.editors.palette.IEntryChangeListener)
*/
public void addEntryChangeListener(final IEntryChangeListener listener) {
_listeners.addIfAbsent(listener);
}
/*
* (non-Javadoc)
*
* @see com.sybase.stf.jmt.pagedesigner.editors.palette.IPaletteItemManager#removeEntryChangeListener(com.sybase.stf.jmt.pagedesigner.editors.palette.IEntryChangeListener)
*/
public void removeEntryChangeListener(final IEntryChangeListener listener) {
_listeners.remove(listener);
}
/**
* Notify model change event
*
* @param oldDefinitions
* @param newDefinitions
*/
private void fireModelChanged(final List oldDefinitions, final List newDefinitions) {
if (_listeners == null) {
return;
}
for (final Iterator<IEntryChangeListener> it= _listeners.iterator();it.hasNext();){
final IEntryChangeListener listener = it.next();
listener.modelChanged(oldDefinitions, newDefinitions);
}
}
/**
* Informs all paletteItemManagers, except the notifying paletteManager, of updates to the customizations
* All palette viewer roots will be notifed of possible updates
* @param notifyingManager
*/
public static void notifyPaletteItemManagersOfCustomizationsUpdate(final IPaletteItemManager notifyingManager){
boolean hasLock = false;
try {
if (MANAGER_LOCK.tryLock(MANAGER_LOCK_TIMEOUT, TimeUnit.SECONDS)){
hasLock = true;
for (Iterator it=_managers.values().iterator();it.hasNext();){
final PaletteItemManager mgr = (PaletteItemManager)it.next();
if (mgr != null && notifyingManager != mgr)
mgr.reset();
}
}
else {
//if we get here then the lock has timed out
PDPlugin.log(new Status(Status.ERROR, PDPlugin.getPluginId(), "Failed to get managers lock in notifyPaletteItemManagersOfCustomizationsUpdate")); //$NON-NLS-1$
}
} catch (InterruptedException e) {
PDPlugin.log("Failed in notifyPaletteItemManagersOfCustomizationsUpdate", e); //$NON-NLS-1$
} finally {
if (hasLock)
MANAGER_LOCK.unlock();
}
}
public void registryChanged(final TagRegistryChangeEvent event) {
final EventType eventType = event.getType();
switch (eventType) {
case ADDED_NAMESPACE:
addNamespaces(event.getAffectedObjects());
break;
case REMOVED_NAMESPACE:
removeNamespaces(event.getAffectedObjects());
break;
case CHANGED_NAMESPACE:
changeNamespaces(event.getAffectedObjects());
break;
case REGISTRY_DISPOSED:
break;
default:
break;
}
DesignerPaletteCustomizationsHelper.loadUserCustomizations(this);
sortCategories();
fireModelChanged(null, null);
}
private void addNamespaces(final List<? extends Namespace> affectedObjects) {
synchronized (_paletteCategories) {
for (final Namespace ns : affectedObjects) {
if (DEBUG)
System.out.println("Add NS: "+ns.getNSUri()+"["+System.currentTimeMillis()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
_paletteHelper.configPaletteItemsByNamespace(this, ns);
}
}
}
private void removeNamespaces(final List<? extends Namespace> affectedObjects) {
final List<Integer> drawersToRemove = new ArrayList<Integer>();
synchronized (_paletteCategories) {
for (final Namespace ns : affectedObjects) {
for (int i=_paletteCategories.size() - 1; i >= 0; i--) {//gather in reverse order
final PaletteDrawer drawer = _paletteCategories.get(i);
if (drawer.getId().equals(ns.getNSUri())) {
if (DEBUG)
System.out.println("Remove NS: "+drawer.getId() +"["+System.currentTimeMillis()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
drawersToRemove.add(new Integer(i));
}
}
}
if (! drawersToRemove.isEmpty()) {
Collections.sort(drawersToRemove, new Comparator<Integer>() {//reverse order sort
public int compare(Integer o1, Integer o2) {
if (o1.intValue() > o2.intValue())
return -1;
else if (o1.intValue() < o2.intValue())
return 1;
return 0;
}
});
for (Integer index : drawersToRemove) {
_paletteCategories.remove(index.intValue());
}
}
}
}
private void changeNamespaces(final List<? extends Namespace> affectedObjects) {
//for now, remove then add
removeNamespaces(affectedObjects);
addNamespaces(affectedObjects);
}
public TagRegistryIdentifier getTagRegistryIdentifier() {
return _tagRegId;
}
/**
* @return helper
*/
public PaletteHelper getPaletteHelper() {
return _paletteHelper;
}
}