/** * Copyright (c) 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.apache.trinidad.tagsupport.converter.operations; import java.io.ByteArrayInputStream; import java.io.InputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.jst.pagedesigner.dtmanager.converter.ITransformOperation; import org.eclipse.jst.pagedesigner.dtmanager.converter.operations.TransformOperationFactory; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; /** * ITransformOperation implementation specifically for the "outputFormatted" JSF * Element. * * <br><b>Note:</b> requires ITransformOperation.setTagConverterContext(...) to * have been called to provide a valid ITagConverterContext instance prior to * a call to the transform(...) method. * * @author Ian Trimble - Oracle */ public class OutputFormattedOperation extends AbstractTrinidadTransformOperation { /* * NOTICE (especially if looking for missing whitespace): * Because this operation can potentially return a mix of child Element * and Text nodes inside a span Element, it can suffer from bug #221629 * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=221629). * This operation is not the cause of the above-mentioned bug. */ private static final String STYLECLASS_INSTRUCTION = "AFInstructionText"; //$NON-NLS-1$ private static final String STYLECLASS_PAGESTAMP = "OraPageStampText"; //$NON-NLS-1$ private static final String STYLECLASS_INCONTEXTBRANDING = "p_InContextBrandingText"; //$NON-NLS-1$ /* (non-Javadoc) * @see org.eclipse.jst.pagedesigner.dtmanager.converter.operations.AbstractTransformOperation#transform(org.w3c.dom.Element, org.w3c.dom.Element) */ @Override public Element transform(Element srcElement, Element curElement) { //create outer span element and set class attribute Element spanElement = createElement("span"); //$NON-NLS-1$ ITransformOperation operation = TransformOperationFactory.getInstance().getTransformOperation( TransformOperationFactory.OP_CopyAttributeWithRenameOperation, new String[]{"inlineStyle", "style"}); //$NON-NLS-1$ //$NON-NLS-2$ operation.transform(srcElement, spanElement); String styleClass = srcElement.getAttribute("styleClass"); //$NON-NLS-1$ if (styleClass == null || styleClass.length() < 1) { String styleUsage = srcElement.getAttribute("styleUsage"); //$NON-NLS-1$ if (styleUsage != null && styleUsage.length() > 8) { if (styleUsage.equals("instruction")) { //$NON-NLS-1$ styleClass = STYLECLASS_INSTRUCTION; } else if (styleUsage.equals("pageStamp")) { //$NON-NLS-1$ styleClass = STYLECLASS_PAGESTAMP; } else if (styleUsage.equals("inContextBranding")) { //$NON-NLS-1$ styleClass = STYLECLASS_INCONTEXTBRANDING; } } } if (styleClass != null && styleClass.length() > 0) { appendAttribute(spanElement, "class", styleClass); //$NON-NLS-1$ } //deal with value String value = srcElement.getAttribute("value"); //$NON-NLS-1$ if (value != null && value.length() > 0) { StringBuffer wrappedValue = new StringBuffer(); wrappedValue.append("<?xml version=\"1.0\"?><value>"); //$NON-NLS-1$ wrappedValue.append(value); wrappedValue.append("</value>"); //$NON-NLS-1$ InputStream inputStream = new ByteArrayInputStream(wrappedValue.toString().getBytes()); Element valueElement = getValueDocumentElement(inputStream); if (valueElement != null) { if (!appendValueNodes(spanElement, valueElement)) { //remove any children added before appendValueNodes failed NodeList childNodes = spanElement.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { spanElement.removeChild(childNodes.item(i)); } //set as simple text appendChildText(value, spanElement); } } else { //set as simple text appendChildText(value, spanElement); } } return spanElement; } private Element getValueDocumentElement(InputStream inputStream) { Element element = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); //TODO: entity expansion should be set to false for ".jsp", true for ".jspx" factory.setExpandEntityReferences(true); try { DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(inputStream); element = document.getDocumentElement(); } catch(Exception e) { //fail on any exception - text with markup will be rendered instead } return element; } private boolean appendValueNodes(Node parentNode, Node currentNode) { boolean success = true; try { NodeList childNodes = currentNode.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node childNode = childNodes.item(i); if (childNode instanceof Element) { //TODO: elements should be filtered to only create elements specified for this tag Element newElement = (Element)parentNode.appendChild( parentNode.getOwnerDocument().createElement( childNode.getNodeName())); NamedNodeMap attrMap = childNode.getAttributes(); if (attrMap != null) { for (int j = 0; j < attrMap.getLength(); j++) { Attr attr = (Attr)attrMap.item(j); //TODO: attributes should be filtered to only create attributes specified for this tag newElement.setAttribute( attr.getName(), attr.getValue()); } } success &= appendValueNodes(newElement, childNode); } else if (childNode instanceof Text) { parentNode.appendChild( parentNode.getOwnerDocument().createTextNode( childNode.getTextContent())); } } } catch(Exception e) { //fail on any exception - text with markup will be rendered instead success = false; } return success; } }