/******************************************************************************* * 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; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.jobs.ILock; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.ITagElement; import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.Namespace; import org.eclipse.jst.jsf.designtime.internal.view.model.jsp.persistence.SerializableTLDNamespace; 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.wst.xml.core.internal.contentmodel.CMNamedNodeMap; import org.eclipse.wst.xml.core.internal.contentmodel.CMNode; /** * Represents a JSP tag library namespace * * @author cbateman * */ public class TLDNamespace extends Namespace { /** * */ private static final long serialVersionUID = 9206460825737988441L; private TLDNamespaceData _tldData; /** * @param tldDoc * @param tagResolver */ public TLDNamespace( final TLDDocument tldDoc, final ITagResolvingStrategy<TLDElementDeclaration, String> tagResolver) { _tldData = new DocumentTLDNamespaceData(tldDoc, tagResolver); } @Override public final String getDisplayName() { return _tldData.getDisplayName(); } @Override public final String getNSUri() { return _tldData.getUri(); } @Override public boolean isInitialized() { return _tldData.isInitialized(); } public final ITagElement getViewElement(final String name) { // // bias the table small if this creates it // final Map<String, ITagElement> tags = getOrCreateMap(3); // // return _tldData.getOrCreateTagElement(name, tags); return _tldData.getViewElement(name); } @Override public final Collection<? extends ITagElement> getViewElements() { return Collections.unmodifiableCollection(_tldData.getAllViewElements().values()); } /** * Differs from getViewElements because it won't include those elements * that haven't been lazily loaded. * * @return an unmodifiable map of all elements currently calculated for * this namespaces */ public final Map<String, ITagElement> getCurrentElements() { return Collections.unmodifiableMap(_tldData.getCurrentElements()); } @Override public final boolean hasViewElements() { return _tldData.getNumTags() > 0; } private Object writeReplace() { return new SerializableTLDNamespace(this); } private static class DocumentTLDNamespaceData extends TLDNamespaceData { /** * */ private static final long serialVersionUID = -1098872687238068584L; private final transient TLDDocument _tldDoc; private final transient ITagResolvingStrategy<TLDElementDeclaration, String> _tagResolver; private final transient ILock _tagMapReadWriteLock; // should only be accessed directly in getOrCreateMap; private Map<String, ITagElement> _tags; // lazy init in initializeTags public DocumentTLDNamespaceData( final TLDDocument tldDoc, final ITagResolvingStrategy<TLDElementDeclaration, String> tagResolver) { _tldDoc = tldDoc; _tagResolver = tagResolver; _tagMapReadWriteLock = Job.getJobManager().newLock(); } @Override public String getDisplayName() { String displayName = _tldDoc.getDisplayName(); if (displayName == null || "".equals(displayName.trim())) //$NON-NLS-1$ { displayName = getUri(); } return displayName; } @Override public String getUri() { return _tldDoc.getUri(); } @Override public int getNumTags() { return _tldDoc.getElements().getLength(); } private ITagElement createTagElement(final String name) { CMNode cmNode = _tldDoc.getElements().getNamedItem(name); ITagElement tagElement = null; if (cmNode instanceof TLDElementDeclaration) { tagElement = _tagResolver .resolve((TLDElementDeclaration) cmNode); if (tagElement == _tagResolver.getNotFoundIndicator()) { // the not-found indicator may not be null. tagElement = null; } } return tagElement; } public final Map<String, ITagElement> getAllViewElements() { final CMNamedNodeMap elementMap = _tldDoc.getElements(); final Map<String, ITagElement> tags = getOrCreateMap(elementMap .getLength()); if (!isInitialized()) { // if we create the table with this call, bias the table size // to be 1 bigger than the number of tags, since we know if it's // smaller, it'll cause at least one buffer resize for (int i = 0; i < elementMap.getLength(); i++) { final CMNode cmNode = elementMap.item(i); getViewElement(cmNode.getNodeName()); } } return tags; } public ITagElement getViewElement(final String name) { ITagElement tagElement = null; _tagMapReadWriteLock.acquire(); final Map<String, ITagElement> tags = getOrCreateMap(_tldDoc.getElements().getLength()); try { tagElement = tags.get(name); if (tagElement == null) { tagElement = createTagElement(name); if (tagElement != null) { tags.put(tagElement.getName(), tagElement); } } } finally { _tagMapReadWriteLock.release(); } return tagElement; } /** * mapSizeHint will be used * * @param mapSizeHint * @return the map */ private synchronized Map<String, ITagElement> getOrCreateMap( final int mapSizeHint) { if (_tags == null) { final float loadFactor = 0.75f; final int initSize = ((int) (mapSizeHint / loadFactor)) + 1; _tags = Collections .synchronizedMap(new HashMap<String, ITagElement>( initSize)); } return _tags; } @Override public boolean isInitialized() { int numTags = getNumTags(); int tagMapSize = getOrCreateMap(3).size(); // we are only initialized if the tag map is the equal in size // to the number of tags in the tld. return numTags == tagMapSize; } @Override public Map<String, ITagElement> getCurrentElements() { return getOrCreateMap(3); } } /** * Encapsulates all the data for a TLDNamespace. Allows the model * to be separated from the Namespace interface for ease of serialization and * controlled subclassing. * */ public abstract static class TLDNamespaceData implements Serializable { /** * */ private static final long serialVersionUID = -1284294636936289804L; /** * @return the displayb */ public abstract String getDisplayName(); // public abstract ITagElement getOrCreateTagElement(String name, // Map<String, ITagElement> tags); /** * @return the number of tags */ public abstract int getNumTags(); /** * @return the namespace uri */ public abstract String getUri(); /** * @param name * @return the view element for name or null if not found. */ public abstract ITagElement getViewElement(final String name); /** * May be long running since it will lazily calculate all unloaded * tags. * @return all view elements for this namespace */ public abstract Map<String, ITagElement> getAllViewElements(); /** * @return true if all elements have been lazily loaded */ public abstract boolean isInitialized(); /** * @return just the currently loaded elements for this namespace. */ public abstract Map<String, ITagElement> getCurrentElements(); } }