blob: b2248085dbfffcaf2f1dd215b31b88d59622cf0e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2008 Oracle 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:
* Oracle Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.jsf.designtime.internal.view.model.jsp.registry;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jst.jsf.common.internal.managedobject.IManagedObject;
import org.eclipse.jst.jsf.common.internal.policy.IdentifierOrderedIteratorPolicy;
import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.Namespace;
import org.eclipse.jst.jsf.core.internal.JSFCorePlugin;
import org.eclipse.jst.jsf.core.internal.JSFCoreTraceOptions;
import org.eclipse.jst.jsf.designtime.internal.Messages;
import org.eclipse.jst.jsf.designtime.internal.view.model.AbstractTagRegistry;
import org.eclipse.jst.jsf.designtime.internal.view.model.jsp.CompositeTagResolvingStrategy;
import org.eclipse.jst.jsf.designtime.internal.view.model.jsp.DefaultJSPTagResolver;
import org.eclipse.jst.jsf.designtime.internal.view.model.jsp.TLDNamespace;
import org.eclipse.jst.jsf.designtime.internal.view.model.jsp.TagIntrospectingStrategy;
import org.eclipse.jst.jsf.designtime.internal.view.model.jsp.UnresolvedJSPTagResolvingStrategy;
import org.eclipse.jst.jsf.designtime.internal.view.model.jsp.persistence.PersistedDataTagStrategy;
import org.eclipse.jst.jsp.core.internal.contentmodel.tld.CMDocumentFactoryTLD;
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.taglib.ITLDRecord;
import org.eclipse.jst.jsp.core.taglib.ITaglibRecord;
import org.eclipse.jst.jsp.core.taglib.TaglibIndex;
/**
* Registry of all tld-defined tags for a particular project classpath
*
* @author cbateman
*
*/
public final class TLDTagRegistry extends AbstractTagRegistry implements
IManagedObject
{
// INSTANCE
private final IProject _project;
private final Map<String, TLDNamespace> _nsResolved;
private final CompositeTagResolvingStrategy<TLDElementDeclaration> _resolver;
private boolean _hasBeenInitialized = false;
private final ConcurrentLinkedQueue<LibraryOperation> _changeOperations = new ConcurrentLinkedQueue<LibraryOperation>();
private final Job _changeJob;
private final PersistedDataTagStrategy _persistedTagStrategy;
private TagIndexListener _tagIndexListener;
private final TLDRegistryPreferences _prefs;
private final MyPropertyListener _myPropertyListener;
TLDTagRegistry(final IProject project)
{
_project = project;
_nsResolved = new HashMap<String, TLDNamespace>();
_prefs = new TLDRegistryPreferences(JSFCorePlugin.getDefault().getPreferenceStore());
_myPropertyListener = new MyPropertyListener();
_prefs.addListener(_myPropertyListener);
_prefs.load();
final IdentifierOrderedIteratorPolicy<String> policy =
getTagResolvingPolicy();
_resolver = new CompositeTagResolvingStrategy<TLDElementDeclaration>(
policy);
// add the strategies
_resolver.addStrategy(new TagIntrospectingStrategy(_project));
_resolver.addStrategy(new DefaultJSPTagResolver(_project));
// makes sure that a tag element will always be created for any
// given tag definition even if other methods fail
_resolver.addStrategy(new UnresolvedJSPTagResolvingStrategy());
_persistedTagStrategy = new PersistedDataTagStrategy(_project);
_persistedTagStrategy.init();
_resolver.addStrategy(_persistedTagStrategy);
_changeJob = new ChangeJob(project.getName());
}
private IdentifierOrderedIteratorPolicy<String> getTagResolvingPolicy()
{
// strategy ordering
final List<String> prefOrdering = _prefs.getEnabledIds();
final List<String> strategyOrdering = new ArrayList<String>(prefOrdering);
// this strategy must always be here, always last
strategyOrdering.add(UnresolvedJSPTagResolvingStrategy.ID);
final IdentifierOrderedIteratorPolicy<String> policy = new IdentifierOrderedIteratorPolicy<String>(
strategyOrdering);
// exclude things that are not explicitly listed in the policy. That
// way preference-based disablement will cause those strategies to
// be excluded.
policy.setExcludeNonExplicitValues(true);
return policy;
}
protected final void doDispose()
{
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions.log("TLDTagRegistry: Disposing for project " //$NON-NLS-1$
+ _project.toString());
}
// call checkpoint to flush serializable data
checkpoint();
//_persistedTagStrategy.dispose();
_nsResolved.clear();
_changeOperations.clear();
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions
.log("TLDTagRegistry: Done disposing registry for " //$NON-NLS-1$
+ _project.toString());
}
}
@Override
protected void cleanupPersistentState()
{
// TODO
}
public synchronized void checkpoint()
{
try
{
_persistedTagStrategy.save(_nsResolved);
}
catch (IOException e)
{
JSFCorePlugin.log(e, "Checkpointing JSP tags failed"); //$NON-NLS-1$
}
catch (ClassNotFoundException e)
{
JSFCorePlugin.log(e, "Checkpointing JSP tags failed"); //$NON-NLS-1$
}
}
@Override
protected Job getRefreshJob(final boolean flushCaches)
{
return new Job(Messages.TLDTagRegistry_RefreshJob + _project.getName())
{
@Override
protected IStatus run(final IProgressMonitor monitor)
{
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions.log("TLDTagRegistry.refresh: start"); //$NON-NLS-1$
}
synchronized (TLDTagRegistry.this)
{
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions
.log("TLDTagRegistry.refresh: start"); //$NON-NLS-1$
}
final List<Namespace> namespaces = new ArrayList(
_nsResolved.values());
if (flushCaches)
{
_persistedTagStrategy.clear();
}
// if we aren't flushing caches, then check point the
// current namespace data, so it isn't lost when we clear
// the namespaces
else
{
checkpoint();
}
_nsResolved.clear();
fireEvent(new TagRegistryChangeEvent(TLDTagRegistry.this,
TagRegistryChangeEvent.EventType.REMOVED_NAMESPACE,
namespaces));
initialize(true);
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions
.log("TLDTagRegistry.refresh: finished"); //$NON-NLS-1$
}
return Status.OK_STATUS;
}
}
};
}
/**
*/
private void initialize(boolean fireEvent)
{
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions.log("TLDTagRegistry.initialize: start"); //$NON-NLS-1$
}
final ITaglibRecord[] tldrecs = TaglibIndex
.getAvailableTaglibRecords(_project.getFullPath());
final List<Namespace> affectedObjects = new ArrayList<Namespace>();
for (final ITaglibRecord tldrec : tldrecs)
{
// defer the event
final Namespace ns = initialize(tldrec, fireEvent);
if (ns != null)
{
affectedObjects.add(ns);
}
}
_hasBeenInitialized = true;
// if tag index listener does exist, add it
if (_tagIndexListener == null)
{
if (JSFCoreTraceOptions.TRACE_TLDREGISTRYMANAGER)
{
JSFCoreTraceOptions
.log("TLDRegistryManager: installing tag index listener due to create instance for " //$NON-NLS-1$
+ _project.toString());
}
_tagIndexListener = new TagIndexListener(this);
TaglibIndex.addTaglibIndexListener(_tagIndexListener);
}
// if (affectedObjects.size() > 0)
// {
// fireEvent(new TagRegistryChangeEvent(this,
// TagRegistryChangeEvent.EventType.ADDED_NAMESPACE,
// affectedObjects));
// }
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions.log("TLDTagRegistry.initialize: finished"); //$NON-NLS-1$
}
}
TLDNamespace initialize(final ITaglibRecord tagRecord,
final boolean fireEvent)
{
if (tagRecord.getRecordType() == ITLDRecord.URL)
{
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY_CHANGES)
{
JSFCoreTraceOptions.log("TLDTagRegistry.initialize_TagRecord: Initializing new tld record: "+tagRecord.toString()); //$NON-NLS-1$
}
long startTime = 0;
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY_PERF)
{
startTime = System.nanoTime();
}
final CMDocumentFactoryTLD factory = new CMDocumentFactoryTLD();
final TLDDocument doc = (TLDDocument) factory
.createCMDocument(tagRecord);
if (doc != null)
{
final TLDNamespace ns = new TLDNamespace(doc, _resolver);
_nsResolved.put(doc.getUri(), ns);
if (fireEvent)
{
fireEvent(new TagRegistryChangeEvent(this,
TagRegistryChangeEvent.EventType.ADDED_NAMESPACE,
Collections.singletonList(ns)));
}
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY_PERF)
{
System.out.printf("Time to update namespace %s was %d\n", //$NON-NLS-1$
ns.getNSUri(), Long.valueOf(System.nanoTime()
- startTime));
}
return ns;
}
}
else
{
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY_CHANGES)
{
JSFCoreTraceOptions.log("TLDTagRegistry.initialize_TagRecord: Skipping tag record for "+tagRecord.toString()); //$NON-NLS-1$
}
}
// no new namespace
return null;
}
void remove(final ITaglibRecord tagRecord)
{
// this is safer, since we likely fail to create a TLDDocument for
// a tagRecord that has been removed.
final String uri = tagRecord.getDescriptor().getURI();
final TLDNamespace ns = _nsResolved.remove(uri);
if (ns != null)
{
fireEvent(new TagRegistryChangeEvent(this,
TagRegistryChangeEvent.EventType.REMOVED_NAMESPACE,
Collections.singletonList(ns)));
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jst.jsf.designtime.internal.view.model.jsp.registry.ITagRegistry#getAllTagLibraries()
*/
public final synchronized Collection<? extends Namespace> getAllTagLibraries()
{
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions.log("TLDTagRegistry.getAllTagLibraries: start"); //$NON-NLS-1$
}
long startTime = 0;
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY_PERF)
{
startTime = System.nanoTime();
}
if (!_hasBeenInitialized)
{
initialize(false);
}
final Set<TLDNamespace> allTagLibraries = new HashSet<TLDNamespace>();
allTagLibraries.addAll(_nsResolved.values());
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY_PERF)
{
System.out.println("Time to getAllTagLibraries for JSP: "+(System.nanoTime()-startTime)); //$NON-NLS-1$
}
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions
.log("TLDTagRegistry.getAllTagLibraries: finished"); //$NON-NLS-1$
}
return allTagLibraries;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jst.jsf.designtime.internal.view.model.jsp.registry.ITagRegistry#getTagLibrary(java.lang.String)
*/
public final synchronized Namespace getTagLibrary(final String uri)
{
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions.log("TLDTagRegistry.getTagLibrary: start uri=" //$NON-NLS-1$
+ uri);
}
if (!_hasBeenInitialized)
{
initialize(false);
}
final Namespace ns = _nsResolved.get(uri);
if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY)
{
JSFCoreTraceOptions
.log("TLDTagRegistry.getTagLibrary: finished, result=" //$NON-NLS-1$
+ ns.toString());
}
return ns;
}
@Override
public String toString()
{
return String
.format(
"TLDRegistry for project %s, isDisposed=%s, hasBeenInitialized=%s, numberOfNamespace=%d", //$NON-NLS-1$
_project.toString(), Boolean.valueOf(isDisposed()),
Boolean.valueOf(_hasBeenInitialized), Integer
.valueOf(_nsResolved.size()));
}
void addLibraryOperation(final LibraryOperation operation)
{
_changeOperations.add(operation);
_changeJob.schedule();
}
private class ChangeJob extends Job
{
private int _rescheduleTime = -1;
public ChangeJob(final String projectName)
{
super(Messages.TLDTagRegistry_UpdateJob + projectName);
}
@Override
protected IStatus run(final IProgressMonitor monitor)
{
synchronized (TLDTagRegistry.this)
{
_rescheduleTime = -1;
LibraryOperation operation = null;
final MultiStatus multiStatus = new MultiStatus(
JSFCorePlugin.PLUGIN_ID, 0, "Result of change job", //$NON-NLS-1$
new Throwable());
while ((operation = _changeOperations.poll()) != null)
{
_rescheduleTime = 10000; // ms
operation.run();
multiStatus.add(operation.getResult());
}
if (_rescheduleTime >= 0 && !monitor.isCanceled())
{
// if any operations were found on this run, reschedule
// to run again in 10seconds based on the assumption that
// events may be coming in bursts
schedule(_rescheduleTime);
}
return multiStatus;
}
}
}
private class MyPropertyListener extends TLDRegistryPreferences.PropertyListener
{
@Override
public void strategyOrderChanged()
{
synchronized(TLDTagRegistry.this)
{
_prefs.load();
final IdentifierOrderedIteratorPolicy<String> policy =
getTagResolvingPolicy();
_resolver.setPolicy(policy);
}
}
}
}