| /******************************************************************************* |
| * 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; |
| |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.jst.jsf.common.dom.TagIdentifier; |
| import org.eclipse.jst.jsf.common.runtime.internal.model.ViewObject; |
| import org.eclipse.jst.jsf.context.resolver.structureddocument.IStructuredDocumentContextResolverFactory; |
| import org.eclipse.jst.jsf.context.resolver.structureddocument.IWorkspaceContextResolver; |
| import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContext; |
| import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; |
| import org.eclipse.jst.jsf.core.internal.region.Region2ElementAdapter; |
| import org.eclipse.jst.jsf.core.internal.region.Region2ElementAdapter.NoElementException; |
| import org.eclipse.jst.jsf.core.internal.tld.TagIdentifierFactory; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; |
| import org.w3c.dom.Element; |
| |
| /** |
| * @author cbateman |
| * |
| */ |
| public final class XMLViewObjectMappingService implements Serializable |
| { |
| /** |
| * |
| */ |
| private static final long serialVersionUID = -5371998199186683997L; |
| private final Map<ElementData, ViewObject> _elementToViewObjMap; |
| private final Map<ViewObject, ElementData> _viewObjToElementMap; |
| |
| /** |
| * |
| */ |
| public XMLViewObjectMappingService() |
| { |
| _elementToViewObjMap = new HashMap<ElementData, ViewObject>(); |
| _viewObjToElementMap = new HashMap<ViewObject, ElementData>(); |
| } |
| |
| /** |
| * @param elementData |
| * @param viewObject |
| */ |
| public void createMapping(final ElementData elementData, |
| final ViewObject viewObject) |
| { |
| if (elementData == null) |
| { |
| throw new IllegalArgumentException("elementData cannot be null"); //$NON-NLS-1$ |
| } |
| else if (viewObject == null) |
| { |
| throw new IllegalArgumentException("viewObject cannot be null"); //$NON-NLS-1$ |
| } |
| |
| synchronized(this) |
| { |
| _elementToViewObjMap.put(elementData, viewObject); |
| _viewObjToElementMap.put(viewObject, elementData); |
| } |
| } |
| |
| /** |
| * Remove all mappings |
| */ |
| public void clearMappings() |
| { |
| synchronized (this) |
| { |
| _elementToViewObjMap.clear(); |
| _viewObjToElementMap.clear(); |
| } |
| } |
| |
| /** |
| * @param elementData |
| * @return the view object for which the mapping was removed |
| */ |
| public ViewObject removeMapping(final ElementData elementData) |
| { |
| if (elementData == null) |
| { |
| throw new IllegalArgumentException("elementData mustn't be null"); //$NON-NLS-1$ |
| } |
| |
| ViewObject viewObject = null; |
| synchronized(this) |
| { |
| viewObject = _elementToViewObjMap.remove(elementData); |
| if (viewObject != null) |
| { |
| _viewObjToElementMap.remove(viewObject); |
| } |
| } |
| return viewObject; |
| } |
| |
| /** |
| * @param viewObject |
| * @return the element data for which the mapping was removed |
| */ |
| public ElementData removeMapping(final ViewObject viewObject) |
| { |
| if (viewObject == null) |
| { |
| throw new IllegalArgumentException("elementData mustn't be null"); //$NON-NLS-1$ |
| } |
| |
| ElementData elementData = null; |
| synchronized(this) |
| { |
| elementData = _viewObjToElementMap.remove(viewObject); |
| if (elementData != null) |
| { |
| _elementToViewObjMap.remove(elementData); |
| } |
| } |
| return elementData; |
| } |
| |
| /** |
| * @param viewObject |
| * @return the element data for the view object |
| */ |
| public synchronized ElementData findElementData(final ViewObject viewObject) |
| { |
| return _viewObjToElementMap.get(viewObject); |
| } |
| |
| /** |
| * @param viewObject |
| * @return the element for the viewObject |
| */ |
| public Element findElement(final ViewObject viewObject) |
| { |
| final ElementData elementData = findElementData(viewObject); |
| |
| if (elementData != null) |
| { |
| final IFile file = ResourcesPlugin.getWorkspace().getRoot() |
| .getFile(new Path(elementData.getDocumentPath())); |
| |
| if (file != null && file.isAccessible()) |
| { |
| IStructuredModel model = null; |
| try |
| { |
| model = StructuredModelManager.getModelManager() |
| .getModelForRead(file); |
| final IStructuredDocument sDoc = model |
| .getStructuredDocument(); |
| final IStructuredDocumentRegion region = sDoc |
| .getRegionAtCharacterOffset(elementData |
| .getStartOffset()); |
| final Region2ElementAdapter adapter = new Region2ElementAdapter( |
| region); |
| final TagIdentifier tagId = adapter.getTagId(); |
| if (tagId.equals(elementData.getTagId())) |
| { |
| return adapter.getElement(); |
| } |
| } |
| catch (CoreException ce) |
| { |
| JSFCorePlugin.log(ce, |
| "While finding element for viewObject: " //$NON-NLS-1$ |
| + viewObject.toString() + " in " //$NON-NLS-1$ |
| + file.getFullPath()); |
| } |
| catch (IOException e) |
| { |
| JSFCorePlugin.log(e, |
| "While finding element for viewObject: " //$NON-NLS-1$ |
| + viewObject.toString() + " in " //$NON-NLS-1$ |
| + file.getFullPath()); |
| } |
| catch (NoElementException e) |
| { |
| JSFCorePlugin.log(e, |
| "While finding element for viewObject: " //$NON-NLS-1$ |
| + viewObject.toString() + " in " //$NON-NLS-1$ |
| + file.getFullPath()); |
| } |
| finally |
| { |
| if (model != null) |
| { |
| model.releaseFromRead(); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @param elementData |
| * @return the mapped view object for elementData or null if not found. |
| */ |
| public synchronized ViewObject findViewObject(final ElementData elementData) |
| { |
| return _elementToViewObjMap.get(elementData); |
| } |
| |
| /** |
| * @param namespace |
| * @param tagName |
| * @param context |
| * @param attributeToPropertyMap |
| * @return a new Element data for the namespace/element name in 'context' |
| */ |
| public static ElementData createElementData(final String namespace, |
| final String tagName, final IStructuredDocumentContext context, |
| final Map<String, String> attributeToPropertyMap) |
| { |
| final IFile file = getFile(context); |
| final int offset = context.getDocumentPosition(); |
| |
| if (file != null && file.isAccessible() && offset > -1 |
| && tagName != null && namespace != null) |
| { |
| return new ElementData(offset, file.getFullPath().toString(), |
| TagIdentifierFactory |
| .createJSPTagWrapper(namespace, tagName), |
| attributeToPropertyMap); |
| } |
| return null; |
| } |
| |
| private static IFile getFile(final IStructuredDocumentContext context) |
| { |
| final IWorkspaceContextResolver resolver = IStructuredDocumentContextResolverFactory.INSTANCE |
| .getWorkspaceContextResolver(context); |
| IResource res = resolver.getResource(); |
| if (res instanceof IFile) |
| { |
| return (IFile) res; |
| } |
| // fall through. |
| return null; |
| } |
| |
| /** |
| * Describes enough information about an element instance to look it up |
| * again, without storing possibly transient references to it. |
| * |
| * @author cbateman |
| * |
| */ |
| public final static class ElementData implements Serializable |
| { |
| /** |
| * serializable id |
| */ |
| private static final long serialVersionUID = 7937312530318827977L; |
| |
| private transient TagIdentifier _tagId; |
| private Map<String, String> _attributeToPropertyMap; |
| private String _documentPath; |
| private int _startOffset; |
| |
| /** |
| * @param startOffset |
| * @param documentPath |
| * @param tagId |
| * @param attributeToPropertyMap constructor takes copy of map |
| */ |
| private ElementData(final int startOffset, final String documentPath, |
| final TagIdentifier tagId, Map<String, String> attributeToPropertyMap) |
| { |
| super(); |
| _startOffset = startOffset; |
| |
| _tagId = tagId; |
| _documentPath = documentPath; |
| _attributeToPropertyMap = Collections.unmodifiableMap( |
| new HashMap<String,String>(attributeToPropertyMap)); |
| } |
| |
| /** |
| * Default constructor |
| */ |
| protected ElementData() |
| { |
| // for serialization |
| } |
| |
| /** |
| * @return the tag identifier for this element |
| */ |
| public final TagIdentifier getTagId() |
| { |
| return _tagId; |
| } |
| |
| /** |
| * @param forTagAttribute |
| * @return the name of the view object property that forTagAttribute |
| * maps to on this element or null if none. |
| */ |
| public final String getPropertyName(final String forTagAttribute) |
| { |
| return _attributeToPropertyMap.get(forTagAttribute); |
| } |
| |
| final int getStartOffset() |
| { |
| return _startOffset; |
| } |
| |
| final String getDocumentPath() |
| { |
| return _documentPath; |
| } |
| |
| private void writeObject(final java.io.ObjectOutputStream out) |
| throws IOException |
| { |
| out.defaultWriteObject(); |
| out.writeObject(_tagId.getUri()); |
| out.writeObject(_tagId.getTagName()); |
| } |
| |
| private void readObject(final java.io.ObjectInputStream in) |
| throws IOException, ClassNotFoundException |
| { |
| in.defaultReadObject(); |
| final String uri = (String) in.readObject(); |
| final String tagName = (String) in.readObject(); |
| _tagId = TagIdentifierFactory.createJSPTagWrapper(uri, tagName); |
| } |
| |
| @Override |
| public boolean equals(final Object obj) |
| { |
| if (this == obj) |
| { |
| return true; |
| } |
| if (obj instanceof ElementData) |
| { |
| final ElementData other = (ElementData) obj; |
| return _startOffset == other._startOffset |
| && _documentPath.equals(other._documentPath) |
| && _tagId.equals(other._tagId); |
| } |
| return false; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| int hashCode = _tagId.hashCode(); |
| hashCode ^= _documentPath.hashCode(); |
| // startOffsets will generally fit in the first 10 bits, so mix |
| // it up a bit. |
| hashCode ^= ~(_startOffset * 104551); |
| return hashCode; |
| } |
| } |
| } |