package org.eclipse.jst.jsf.facelet.core.internal.registry.taglib; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jst.jsf.common.internal.util.JarUtilities; import org.eclipse.jst.jsf.designtime.internal.resources.IJSFResource; import org.eclipse.jst.jsf.designtime.internal.resources.IJSFResourceContainer; import org.eclipse.jst.jsf.designtime.internal.resources.IJSFResourceFragment; import org.eclipse.jst.jsf.designtime.internal.resources.IJarBasedJSFResource; import org.eclipse.jst.jsf.designtime.internal.resources.IWorkspaceJSFResourceFragment; import org.eclipse.jst.jsf.designtime.internal.resources.WorkspaceJSFResourceContainer; import org.eclipse.jst.jsf.facelet.core.internal.registry.taglib.Listener.TaglibChangedEvent; import org.eclipse.jst.jsf.facelet.core.internal.registry.taglib.Listener.TaglibChangedEvent.CHANGE_TYPE; import org.eclipse.jst.jsf.facelet.core.internal.registry.taglib.faceletTaglib.FaceletTaglibFactory; import org.eclipse.jst.jsf.facelet.core.internal.registry.taglib.faceletTaglib.FaceletTaglibTag; /** * A facelet tag record is derived from a JSF locatable resource (ezcomp). * * @author cbateman * */ public class JSFResourceBasedTagRecord extends FaceletTagRecord { private final List<FaceletTaglibTag> _tags; private final String _uri; /** * @param uri * @param tags * @param descriptor */ public JSFResourceBasedTagRecord(final String uri, final List<FaceletTaglibTag> tags, final TagRecordDescriptor descriptor) { super(descriptor); _uri = uri; _tags = tags; } /** * */ private static final long serialVersionUID = 5944923828112777373L; public int getNumTags() { return _tags.size(); } @Override public String getURI() { return _uri; } @Override public FaceletTaglibTag getTag(final String name) { for (final FaceletTaglibTag tag : _tags) { if (tag.getTagName().equals(name)) { return tag; } } return null; } @Override public Collection<? extends FaceletTaglibTag> getTags() { return Collections.unmodifiableCollection(_tags); } /** * Merge my tags with 'withThese'. Tags in my list will be replaced with * those in 'withThese' if their names match. * * @param withThese */ /* package */JSFResourceBasedTagRecord mergeTags( final List<FaceletTaglibTag> withThese) { final List<FaceletTaglibTag> tags = new ArrayList<FaceletTaglibTag>( _tags); for (final FaceletTaglibTag mergeTag : withThese) { final FaceletTaglibTag tag = getTag(mergeTag.getTagName()); if (tag != null) { tags.remove(tag); } tags.add(mergeTag); } return new JSFResourceBasedTagRecord(_uri, tags, getDescriptor()); } /* package */JSFResourceBasedTagRecord removeTags( final List<FaceletTaglibTag> removeThese) { final List<FaceletTaglibTag> tags = new ArrayList<FaceletTaglibTag>( _tags); for (final FaceletTaglibTag mergeTag : removeThese) { final FaceletTaglibTag tag = getTag(mergeTag.getTagName()); if (tag != null) { tags.remove(tag); } } return new JSFResourceBasedTagRecord(_uri, tags, getDescriptor()); } /** * A builder for tag record. * * @author cbateman * */ public static class Builder { private static final LibEntry WHOLE_LIBRARY = new LibEntry(null); private static final JSFResourceBasedTagRecord WHOLE_LIB_RECORD = new JSFResourceBasedTagRecord(null, Collections.EMPTY_LIST, null); private final Map<String, LibEntry> _tags = new HashMap<String, LibEntry>(); private static final String FACELET_FILE_CONTENT_TYPE = "org.eclipse.wst.html.core.htmlsource"; //$NON-NLS-1$ private static final String[] COMPOSITE_LIB_NS_PREFIXES = new String[] { "http://java.sun.com/jsf/composite/", //$NON-NLS-1$ "http://xmlns.jcp.org/jsf/composite/" //$NON-NLS-1$ }; /** * @param jsfResource * @param changeType */ public void addTag(final IJSFResource jsfResource, final CHANGE_TYPE changeType) { final String libraryName = jsfResource.getId().getLibraryName(); if (libraryName == null) { return; } for (String nsPrefix: COMPOSITE_LIB_NS_PREFIXES) { final String uri = String.format("%s%s", nsPrefix, libraryName); //$NON-NLS-1$ LibEntry tags = _tags.get(uri); if (tags == null) { tags = new LibEntry(createDescriptor(jsfResource)); _tags.put(uri, tags); } final String resourceName = jsfResource.getId().getResourceName(); final IPath resourceNamePath = new Path(resourceName) .removeFileExtension(); final FaceletTaglibTag tag = FaceletTaglibFactory.eINSTANCE .createFaceletTaglibTag(); tag.setTagName(resourceNamePath.toString()); switch (changeType) { case ADDED: case CHANGED: // only add to the list on a add/change if the resource // exists and is the right type // Bug 377405: order the isContentType call first because // it has a high probability of returning false and // short-circuiting the isAccessible call that can // be very expensive (10-100 times) if (jsfResource .isContentType(FACELET_FILE_CONTENT_TYPE) && jsfResource.isAccessible()) { tags.addTag(tag); } break; case REMOVED: // add all comers to the remove list. There will only be // removal // on merge if ADDED/CHANGED path decided they should be // there. tags.addTag(tag); break; } } } /** * @param jsfResource * @param changeType */ public void addLibrary(final IJSFResourceContainer jsfResource, final CHANGE_TYPE changeType) { final String libraryName = jsfResource.getId().getLibraryName(); if (libraryName == null || libraryName.trim().length() == 0) { return; } for (String nsPrefix: COMPOSITE_LIB_NS_PREFIXES) { final String uri = String.format("%s%s", nsPrefix, libraryName); //$NON-NLS-1$ if (changeType == CHANGE_TYPE.REMOVED) { _tags.put(uri, WHOLE_LIBRARY); } else { LibEntry tags = _tags.get(uri); if (tags == null) { tags = new LibEntry(createDescriptor(jsfResource)); _tags.put(uri, tags); } } } } /** * @return the built list of tag records. */ public Map<String, JSFResourceBasedTagRecord> build() { final Map<String, JSFResourceBasedTagRecord> records = new HashMap<String, JSFResourceBasedTagRecord>(); for (final Map.Entry<String, LibEntry> entry : _tags .entrySet()) { if (entry.getValue() == WHOLE_LIBRARY) { records.put(entry.getKey(), WHOLE_LIB_RECORD); } else { final String uri = entry.getKey(); final List<FaceletTaglibTag> tags = new ArrayList<FaceletTaglibTag>(entry.getValue().getTags()); final TagRecordDescriptor descriptor = entry.getValue().getDescriptor(); final JSFResourceBasedTagRecord newRecord = new JSFResourceBasedTagRecord( uri, tags, descriptor); records.put(entry.getKey(), newRecord); } } return records; } /** * @param locator * @param records * @return a list of taglib change events that reflect what will happen * when my tags are merged into the map 'records'. */ public List<TaglibChangedEvent> createMerge( final AbstractFaceletTaglibLocator locator, final Map<String, JSFResourceBasedTagRecord> records) { final Map<String, JSFResourceBasedTagRecord> newRecords = build(); final List<TaglibChangedEvent> mergeEvents = new ArrayList<TaglibChangedEvent>(); for (final Map.Entry<String, JSFResourceBasedTagRecord> entry : newRecords .entrySet()) { TaglibChangedEvent event = null; if (!records.containsKey(entry.getKey())) { event = new TaglibChangedEvent(locator, null, entry.getValue(), CHANGE_TYPE.ADDED); } else { final JSFResourceBasedTagRecord oldRecord = records .get(entry.getKey()); final JSFResourceBasedTagRecord newRecord = oldRecord .mergeTags(entry.getValue()._tags); event = new TaglibChangedEvent(locator, oldRecord, newRecord, CHANGE_TYPE.CHANGED); } mergeEvents.add(event); } return mergeEvents; } /** * @param locator * @param records * @return a list of change events that will result from removing my * _tags from records. */ public List<TaglibChangedEvent> createRemove( final AbstractFaceletTaglibLocator locator, final Map<String, JSFResourceBasedTagRecord> records) { final Map<String, JSFResourceBasedTagRecord> newRecords = build(); final List<TaglibChangedEvent> mergeEvents = new ArrayList<TaglibChangedEvent>(); for (final Map.Entry<String, JSFResourceBasedTagRecord> entry : newRecords .entrySet()) { TaglibChangedEvent event = null; final JSFResourceBasedTagRecord oldRecord = records.get(entry .getKey()); if (oldRecord != null) { final JSFResourceBasedTagRecord record = entry.getValue(); if (record == WHOLE_LIB_RECORD) { event = new TaglibChangedEvent(locator, oldRecord, null, CHANGE_TYPE.REMOVED); } else { final JSFResourceBasedTagRecord newRecord = oldRecord .removeTags(entry.getValue()._tags); event = new TaglibChangedEvent(locator, oldRecord, newRecord, CHANGE_TYPE.CHANGED); } } if (event != null) { mergeEvents.add(event); } } return mergeEvents; } /** * @param events * @param withThese * @return a new merged map that contains withThese plus everything * here. */ public Map<String, JSFResourceBasedTagRecord> merge( final List<TaglibChangedEvent> events, final Map<String, JSFResourceBasedTagRecord> withThese) { final Map<String, JSFResourceBasedTagRecord> newMap = new HashMap<String, JSFResourceBasedTagRecord>( withThese); for (final TaglibChangedEvent event : events) { switch (event.getChangeType()) { case ADDED: case CHANGED: { final IFaceletTagRecord newRecord = event.getNewValue(); // doubles as null check if (newRecord instanceof JSFResourceBasedTagRecord) { newMap.put(newRecord.getURI(), (JSFResourceBasedTagRecord) newRecord); } } break; case REMOVED: { final IFaceletTagRecord oldRecord = event.getOldValue(); if (oldRecord != null) { newMap.remove(oldRecord.getURI()); } } break; } } return newMap; } private TagRecordDescriptor createDescriptor(final IJSFResourceFragment resource) { if (resource instanceof IWorkspaceJSFResourceFragment) { if (resource instanceof WorkspaceJSFResourceContainer) { final IResource res = ((WorkspaceJSFResourceContainer)resource).getResource(); return new WorkspaceTagRecordDescriptor((IFolder) res); } final IResource res = ((IWorkspaceJSFResourceFragment) resource) .getResource(); return new WorkspaceTagRecordDescriptor((IFile) res); } else if (resource instanceof IJarBasedJSFResource) { final URL jarURL = ((IJarBasedJSFResource) resource) .getJarURL(); final File file = JarUtilities.INSTANCE.getFile(jarURL); if (file != null) { final String absolutePath = file.getAbsolutePath(); final String jarEntryName = ((IJarBasedJSFResource) resource) .getJarEntryName(); return new JarTagRecordDescriptor(new Path(absolutePath), jarEntryName); } } return null; } private static class LibEntry { private final TagRecordDescriptor _descriptor; private final List<FaceletTaglibTag> _tags; /** * @param descriptor */ public LibEntry(final TagRecordDescriptor descriptor) { super(); _descriptor = descriptor; _tags = new ArrayList<FaceletTaglibTag>(); } public TagRecordDescriptor getDescriptor() { return _descriptor; } public void addTag(final FaceletTaglibTag tag) { _tags.add(tag); } public List<FaceletTaglibTag> getTags() { return Collections.unmodifiableList(_tags); } } } }