blob: 855aad4caa7b99fb5b5456759aeaedce1bc189b7 [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;
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;
}
}
}