blob: fcff0bac0d7bce2a6b39959dd0d186c1a52e59d5 [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.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jst.jsf.common.internal.types.TypeConstants;
import org.eclipse.jst.jsf.common.runtime.internal.model.ViewObject;
import org.eclipse.jst.jsf.common.runtime.internal.model.behavioural.ActionSourceInfo;
import org.eclipse.jst.jsf.common.runtime.internal.model.behavioural.ActionSourceInfo2;
import org.eclipse.jst.jsf.common.runtime.internal.model.behavioural.EditableValueHolderInfo;
import org.eclipse.jst.jsf.common.runtime.internal.model.behavioural.INamingContainerInfo;
import org.eclipse.jst.jsf.common.runtime.internal.model.behavioural.ValueHolderInfo;
import org.eclipse.jst.jsf.common.runtime.internal.model.component.ComponentFactory;
import org.eclipse.jst.jsf.common.runtime.internal.model.component.ComponentInfo;
import org.eclipse.jst.jsf.common.runtime.internal.model.component.ComponentTypeInfo;
import org.eclipse.jst.jsf.common.runtime.internal.model.decorator.ConverterDecorator;
import org.eclipse.jst.jsf.common.runtime.internal.model.decorator.ConverterTypeInfo;
import org.eclipse.jst.jsf.common.runtime.internal.model.decorator.ValidatorDecorator;
import org.eclipse.jst.jsf.common.runtime.internal.model.decorator.ValidatorTypeInfo;
import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.IComponentPropertyHandler;
import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.IComponentTagElement;
import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.IConverterTagElement;
import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.ITagAttributeHandler;
import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.ITagElement;
import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.IValidatorTagElement;
import org.eclipse.jst.jsf.common.util.JDTBeanProperty;
import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContext;
import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContextFactory;
import org.eclipse.jst.jsf.core.internal.JSFCorePlugin;
import org.eclipse.jst.jsf.designtime.internal.view.XMLViewObjectMappingService.ElementData;
import org.eclipse.jst.jsf.designtime.internal.view.mapping.ICustomViewMapper;
import org.eclipse.jst.jsf.designtime.internal.view.mapping.ICustomViewMapper.PropertyMapping;
import org.eclipse.jst.jsf.designtime.internal.view.mapping.viewmapping.CustomViewMapperExtensionLoader;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
/**
* A strategy for constructing view objects.
*
* @author cbateman
*
*/
public class XMLViewObjectConstructionStrategy extends
ViewObjectConstructionStrategy<Element>
{
private static final String GENERATED_ID = "_generatedId"; //$NON-NLS-1$
private final ComponentConstructionData _constructionData;
private final XMLViewDefnAdapter _adapter;
private final XMLViewObjectMappingService _mappingService;
/**
* @param adapter
* MUST NOT BE NULL
* @param constructionData
* MUST NOT BE NULL
* @param mappingService
* MAY BE NULL
*/
public XMLViewObjectConstructionStrategy(final XMLViewDefnAdapter adapter,
final ComponentConstructionData constructionData,
final XMLViewObjectMappingService mappingService)
{
super();
if (adapter == null || constructionData == null)
{
throw new IllegalArgumentException(
"adapter and constructionData must not be null"); //$NON-NLS-1$
}
_constructionData = constructionData;
_adapter = adapter;
_mappingService = mappingService;
}
@Override
public ViewObject createViewObject(final Element element,
final ITagElement tagElement)
{
try
{
if (tagElement instanceof IComponentTagElement)
{
String id = null;
// only generate ids for non-viewroot components. This will
// make the generated id's more faithful to runtime since the
// running count won't be incremented for view roots (as they
// won't at runtime).
final ComponentTypeInfo typeInfo = ((IComponentTagElement) tagElement)
.getComponent();
if (!"javax.faces.ViewRoot".equals(typeInfo.getComponentType())) //$NON-NLS-1$
{
id = calculateId(element, _constructionData);
}
return findBestComponent(tagElement.getUri(), element, id,
(IComponentTagElement) tagElement);
}
else if (tagElement instanceof IConverterTagElement)
{
final ConverterTypeInfo typeInfo = ((IConverterTagElement) tagElement)
.getConverter();
// TODO: validate when no parent
ComponentInfo parent = _constructionData.getParent();
parent = findFirstParent(
ComponentFactory.INTERFACE_VALUEHOLDER, parent);
if (parent != null)
{
parent.addDecorator(
new ConverterDecorator(parent, typeInfo),
ComponentFactory.CONVERTER);
}
// TODO: else validate problem
}
else if (tagElement instanceof IValidatorTagElement)
{
final ValidatorTypeInfo typeInfo = ((IValidatorTagElement) tagElement)
.getValidator();
ComponentInfo parent = _constructionData.getParent();
parent = findFirstParent(
ComponentFactory.INTERFACE_EDITABLEVALUEHOLDER, parent);
if (parent != null)
{
parent.addDecorator(
new ValidatorDecorator(parent, typeInfo),
ComponentFactory.VALIDATOR);
}
// TODO :else validate problem
}
}
catch (final Exception e)
{
// log and ignore if an individual construction fails
JSFCorePlugin.log(e, "Error constructing view object"); //$NON-NLS-1$
}
return null;
}
private ComponentInfo findFirstParent(final String matchingType,
final ComponentInfo start)
{
ComponentInfo parent = start;
while (parent != null && parent.getComponentTypeInfo() != null
&& !parent.getComponentTypeInfo().isInstanceOf(matchingType))
{
parent = parent.getParent();
}
return parent;
}
private ComponentInfo findBestComponent(final String uri,
final Element srcElement, final String id,
final IComponentTagElement tagElement)
{
ComponentInfo bestComponent = null;
final ComponentInfo parent = _constructionData.getParent();
final Map<String, Object> initMap = new HashMap<String, Object>();
final Map<String, String> attributeToPropertyMap = new HashMap<String, String>();
populateInitMap(uri, initMap, srcElement, tagElement,
attributeToPropertyMap);
if (initMap.get("id") == null) //$NON-NLS-1$
{
// id must be set
initMap.put("id", id); //$NON-NLS-1$
}
final ComponentTypeInfo typeInfo = tagElement.getComponent();
// if we have a well-established base type, try that first
// sub-classes must occur before superclasses to ensure most accurate
// detection.
if (typeInfo.isInstanceOf(ComponentFactory.BASE_CLASS_UIINPUT))
{
bestComponent = ComponentFactory.createUIInputInfo(parent,
typeInfo, initMap);
}
else if (typeInfo.isInstanceOf(ComponentFactory.BASE_CLASS_UIOUTPUT))
{
bestComponent = ComponentFactory.createUIOutputInfo(parent,
typeInfo, initMap);
}
else if (typeInfo.isInstanceOf(ComponentFactory.BASE_CLASS_UICOMMAND))
{
bestComponent = ComponentFactory.createUICommandInfo(parent,
typeInfo, initMap);
}
else if (typeInfo.isInstanceOf(ComponentFactory.BASE_CLASS_UIDATA))
{
bestComponent = ComponentFactory.createUIDataInfo(parent, typeInfo,
initMap);
}
else if (typeInfo.isInstanceOf(ComponentFactory.BASE_CLASS_UIFORM))
{
// TODO: how handle prepend ids?
bestComponent = ComponentFactory.createUIFormInfo(parent, typeInfo,
initMap);
}
else
{
// default
bestComponent = ComponentFactory.createComponentInfo(
_constructionData.getParent(), typeInfo, initMap);
}
addTypeAdapters(bestComponent);
maybeMapXMLToViewObjects(bestComponent, srcElement,
attributeToPropertyMap, _constructionData.getDocument());
maybeUpdateViewObject(bestComponent, srcElement, tagElement);
return bestComponent;
}
// TODO: move to view definition adapter?
private void populateInitMap(final String uri,
final Map<String, Object> initMap, final Element srcElement,
final IComponentTagElement tagElement,
final Map<String, String> attributeToPropertyMap)
{
final ComponentTypeInfo typeInfo = tagElement.getComponent();
final Map<String, ITagAttributeHandler> attributeHandlers = tagElement
.getAttributeHandlers();
final NamedNodeMap nodeMap = srcElement.getAttributes();
if (nodeMap != null && attributeHandlers != null)
{
Map<String, JDTBeanProperty> properties = null;
for (int i = 0; i < nodeMap.getLength(); i++)
{
final Attr attr = (Attr) nodeMap.item(i);
if (attr != null)
{
final String name = attr.getLocalName();
if (name != null)
{
final ITagAttributeHandler attrHandler = attributeHandlers
.get(name);
if (attrHandler instanceof IComponentPropertyHandler)
{
if (properties == null)
{
properties = DTComponentIntrospector.getBeanProperties(typeInfo, _constructionData.getProject());
}
mapComponentProperty(uri, srcElement, properties,
(IComponentPropertyHandler) attrHandler,
attr, name, initMap, attributeHandlers,
attributeToPropertyMap);
}
}
}
}
}
}
private void mapComponentProperty(final String uri,
final Element srcElement,
final Map<String, JDTBeanProperty> properties,
final IComponentPropertyHandler attrHandler, final Attr attr,
final String attributeName, final Map initMap,
final Map<String, ITagAttributeHandler> attributeHandlers,
final Map<String, String> attributeToPropertyMap)
{
final String propertyName = attrHandler.getPropertyName();
if (properties.containsKey(propertyName))
{
final String id = attrHandler.getCustomHandler();
ICustomViewMapper mapper = null;
if (id != null)
{
mapper = CustomViewMapperExtensionLoader
.getCustomViewMapper(id);
if (mapper != null)
{
final PropertyMapping mapping = mapper
.mapToComponentProperty(uri, srcElement, attr);
if (mapping != null)
{
initMap.put(mapping.getName(), mapping.getProperty());
attributeToPropertyMap.put(attributeName, mapping
.getName());
return;
}
}
}
final String value = attr.getValue();
if (value != null)
{
final Object convertedValue = convertFromString(value,
properties.get(propertyName));
initMap.put(propertyName, convertedValue);
}
attributeToPropertyMap.put(attributeName, propertyName);
}
}
private void maybeMapXMLToViewObjects(final ViewObject mappedObject,
final Element node,
final Map<String, String> attributeToProperties,
final IDocument document)
{
if (mappedObject != null && _mappingService != null)
{
final String uri = _adapter.getNamespace(node, document);
final IStructuredDocumentContext context = IStructuredDocumentContextFactory.INSTANCE
.getContext(document, node);
final ElementData elementData = XMLViewObjectMappingService
.createElementData(uri, node.getLocalName(), context,
attributeToProperties);
//Bug 269050 - IllegalArgumentException in createMapping() method
if (elementData != null) {
_mappingService.createMapping(elementData, mappedObject);
}
}
}
private void maybeUpdateViewObject(final ComponentInfo bestComponent,
final Element srcElement, final ITagElement tagElement)
{
if (srcElement.getAttributes() == null)
return;
for (int i = 0; i < srcElement.getAttributes().getLength(); i++)
{
final Attr attr = (Attr) srcElement.getAttributes().item(i);
final Map<String, ITagAttributeHandler> attributeHandlers = tagElement
.getAttributeHandlers();
if (attributeHandlers != null)
{
final ITagAttributeHandler handler = attributeHandlers.get(attr
.getLocalName());
if (handler != null)
{
final String id = handler.getCustomHandler();
ICustomViewMapper mapper = null;
if (id != null)
{
mapper = CustomViewMapperExtensionLoader
.getCustomViewMapper(id);
if (mapper != null)
{
mapper.doAttributeActions(bestComponent,
srcElement, attr);
}
}
}
}
}
}
private Object convertFromString(final String convertValue,
final JDTBeanProperty ofThisType)
{
final String signature = ofThisType.getTypeSignature();
Object result = null;
switch (Signature.getTypeSignatureKind(signature))
{
case Signature.BASE_TYPE_SIGNATURE:
result = convertFromBaseType(convertValue, signature);
break;
case Signature.CLASS_TYPE_SIGNATURE:
if (TypeConstants.TYPE_STRING.equals(signature))
{
result = convertValue;
}
else if (TypeConstants.TYPE_JAVAOBJECT.equals(signature))
{
result = convertValue;
}
break;
}
return result;
}
// TODO: does this belong somewhere else?
private Object convertFromBaseType(final String convertValue,
final String signature)
{
if (Signature.SIG_BOOLEAN.equals(signature))
{
return Boolean.valueOf(convertValue);
}
else if (Signature.SIG_INT.equals(signature)
|| Signature.SIG_BYTE.equals(signature)
|| Signature.SIG_SHORT.equals(signature))
{
try
{
return Integer.valueOf(convertValue);
}
catch (final NumberFormatException nfe)
{
return null;
}
}
else if (Signature.SIG_LONG.equals(convertValue))
{
try
{
return Long.valueOf(convertValue);
}
catch (final NumberFormatException nfe)
{
return null;
}
}
return null;
}
private void addTypeAdapters(final ComponentInfo component)
{
final String[] interfaceNames = component.getComponentTypeInfo()
.getInterfaces();
final Set interfaceNameSets = new HashSet();
for (final String interfaceName : interfaceNames)
{
interfaceNameSets.add(interfaceName);
}
// don't replace intrinsic adapters
if (interfaceNameSets.contains(ComponentFactory.INTERFACE_ACTIONSOURCE))
{
// an ActionSource2 is-a ActionSource
if (interfaceNameSets
.contains(ComponentFactory.INTERFACE_ACTIONSOURCE2)
&& component.getAdapter(ComponentFactory.ACTION_SOURCE2) == null)
{
component.addAdapter(ComponentFactory.ACTION_SOURCE2,
new ActionSourceInfo2(null, null, false, null));
}
if (component.getAdapter(ComponentFactory.ACTION_SOURCE) == null)
{
component.addAdapter(ComponentFactory.ACTION_SOURCE,
new ActionSourceInfo(null, null, false));
}
}
if (interfaceNameSets.contains(ComponentFactory.INTERFACE_VALUEHOLDER))
{
// a EditableValueHolder is-a ValueHolder
if (interfaceNameSets
.contains(ComponentFactory.INTERFACE_EDITABLEVALUEHOLDER)
&& component
.getAdapter(ComponentFactory.EDITABLE_VALUE_HOLDER) == null)
{
component.addAdapter(ComponentFactory.EDITABLE_VALUE_HOLDER,
new EditableValueHolderInfo(null, null, null, false,
false, true, false, null, null, null));
}
if (component.getAdapter(ComponentFactory.VALUE_HOLDER) == null)
{
component.addAdapter(ComponentFactory.VALUE_HOLDER,
new ValueHolderInfo(null, null, null));
}
}
if (interfaceNameSets
.contains(ComponentFactory.INTERFACE_NAMINGCONTAINER)
&& component.getAdapter(ComponentFactory.NAMING_CONTAINER) == null)
{
component.addAdapter(ComponentFactory.NAMING_CONTAINER,
INamingContainerInfo.ADAPTER);
}
}
/**
* @param element
* @param constructionData
* @return the id for element either derived from the element using getId or
* if not present, using a generation algorithm
*/
protected String calculateId(final Element element,
final ComponentConstructionData constructionData)
{
final String id = _adapter.getId(element);
if (id != null)
{
return id;
}
// TODO: improve this
final String prefix = _adapter.getGeneratedIdPrefix();
return (prefix != null ? prefix : GENERATED_ID)
+ constructionData.increment();
}
/**
* @return the construction data for this strategy
*/
public final ComponentConstructionData getConstructionData()
{
return _constructionData;
}
}