/******************************************************************************* * 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; } }